Setup

library(data.table)
library(DBI)
library(ggplot2)
library(cowplot)
library(grid)
library(lme4)
library(lmerTest)

Sys.setlocale("LC_TIME", "en_US.UTF-8") # Print English date format
[1] "en_US.UTF-8"
en_US.UTF-8
# Sys.setlocale("LC_TIME", "nl_NL.UTF-8") # Print Dutch date format

number_format <- scales::number_format(big.mark = ",", decimal.mark = ".") # Print English number format
# number_format <- scales::number_format(big.mark = ".", decimal.mark = ",") # Print Dutch number format

theme_paper <- theme_classic(base_size = 12) + 
  theme(axis.text = element_text(colour = "black"),
        panel.grid.major.y = element_line(colour = "grey92"))

School closure and opening dates

Sources:

date_schools_closed <- as.POSIXct("2020-03-16")
date_schools_opened <- as.POSIXct("2020-06-02")

Handle database connections

db_connect <- function() {
  db <- dbConnect(RSQLite::SQLite(), file.path("..", "data", "noordhoff.sqlite"))
  return(db)
}

db_disconnect <- function(db) {
  dbDisconnect(db)
}

Data

The database contains all SlimStampen data collected via Noordhoff’s platform in three courses: Stepping Stones (English), Grandes Lignes (French), and Neue Kontakte (German).

Trial-level response data are stored in the responses table. Book information, such as the course year, book title, and chapter, are stored in the book_info table.

responses

Column Type Explanation
date int UNIX time stamp [s]
user_id chr unique user identifier
method chr course
start_time int elapsed time since session start [ms]
rt int response time [ms]
duration int trial duration [ms]
fact_id int unique fact identifier (within chapter)
correct int response accuracy
answer chr user’s response
choices int number of answer choices (1 == open response)
backspace_used dbl user pressed backspace during trial
backspace_used_first dbl user erased first character of response
study int trial was a study trial
answer_language chr language of the answer
subsession int identifies part within learning session
book_info_id chr unique identifier of book information

book_info

Column Type Explanation
book_info_id chr unique identifier of book information
method_group chr year and edition
book_title chr book title (incl. year, level, edition)
book_type chr type of book
chapter chr chapter number and title

Preview first 10 rows

db <- db_connect()
responses_top <- dbGetQuery(db, "SELECT * FROM responses_noduplicates LIMIT 10")
responses_top
book_info_top <- dbGetQuery(db, "SELECT * FROM book_info LIMIT 10")
book_info_top
db_disconnect(db)

Performance

There are several measures of learning performance we can look at. The most straight-forward of these are response accuracy and response time.

Important factors to keep in mind: question type (multiple choice or open answer) and language. Note that we cannot distinguish between NL-X and X-X, since we only know the language of the answer.

Response accuracy

Whole population

db <- db_connect()
correct <- dbGetQuery(db, 
                      "SELECT r.method AS 'method',
                      DATE(r.date + 3600, 'unixepoch') AS 'doy',
                      r.user_id AS 'user',
                      r.choices > 1 AS 'mcq',
                      r.correct AS 'correct',
                      COUNT(*) AS 'n'
                      FROM 'responses_noduplicates' r
                      WHERE r.study == 0
                      GROUP BY r.method,
                      DATE(r.date + 3600, 'unixepoch'),
                      r.user_id,
                      r.choices > 1,
                      r.correct"
)
setDT(correct)
db_disconnect(db)

Fill in missing rows (where all trials on a day were correct/incorrect):

correct <- tidyr::complete(correct, tidyr::nesting(method, doy, user, mcq), correct, fill = list(n = 0))
setDT(correct)
correct[, mcq := as.logical(mcq)]
accuracy <- correct[, .(accuracy = n[correct == 1]/sum(n), n = sum(n)), by = .(method, doy, user, mcq)]

Add a school year column (cutoff date: 1 August):

accuracy[, doy_posix := as.POSIXct(doy)]
accuracy[, school_year := ifelse(doy_posix < "2019-08-01", "18/19", "19/20")]

Add sensible course names:

accuracy[, course := ifelse(method == "Grandes Lignes", "French", ifelse(method == "Stepping Stones", "English", "German"))]

Align school years:

accuracy[school_year == "18/19", doy_posix_aligned := as.POSIXct(doy_posix + 365*24*60*60, origin = "1970-01-01")]
accuracy[school_year == "19/20", doy_posix_aligned := doy_posix]

Use cut.Date() to bin dates by week. Each day is assigned the date of the most recent Monday.

accuracy[, doy_posix_week := cut.POSIXt(doy_posix, "week")]
accuracy[, doy_posix_aligned_week := cut.POSIXt(doy_posix_aligned, "week")]
accuracy_by_week_and_user <- accuracy[, .(accuracy = sum(accuracy*n)/sum(n)), by = .(course, school_year, doy_posix_aligned_week, user, mcq)]
accuracy_by_week <- accuracy_by_week_and_user[, .(accuracy_mean = mean(accuracy, na.rm = TRUE),
                              accuracy_se = sd(accuracy, na.rm = TRUE)/sqrt(.N), n = .N), by = .(course, school_year, doy_posix_aligned_week, mcq)]

Add question type labels:

accuracy_by_week[, question_type := ifelse(mcq == TRUE, "Multiple\nchoice", "Open\nanswer")]

Plot response accuracy by week (mean +/- 1 SE).

p_acc <- ggplot(accuracy_by_week[(course == "English" & mcq == TRUE) | course == "French",],
            aes(x = as.POSIXct(doy_posix_aligned_week), y = accuracy_mean, group = interaction(school_year, question_type), colour = school_year, fill = school_year)) +
  facet_grid(. ~ course) +
  geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = 0, ymax = 1.05, fill = "grey92", colour = "grey50", lty = 2, alpha = .9) +
  geom_ribbon(aes(ymin = accuracy_mean - accuracy_se, ymax = accuracy_mean + accuracy_se, colour = NULL), alpha = 0.2) +
  geom_line(aes(lty = question_type)) +
  scale_x_datetime(expand = c(0, 0),
                   breaks = as.POSIXct(c(
                     "2019-10-01 02:00:00 CET",
                     "2019-12-01 02:00:00 CET",
                     "2020-02-01 02:00:00 CET",
                     "2020-04-01 02:00:00 CET",
                     "2020-06-01 02:00:00 CET")),
                   limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
                   date_labels = "%b") +
  scale_y_continuous(limits = c(.7, 1), labels = scales::percent_format(accuracy = 1)) +
  scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
  scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
  labs(x = NULL,
       y = "Accuracy",
       colour = "School year",
       fill = "School year",
       lty = "Question type") +
  guides(colour = guide_legend(order = 1),
         fill = guide_legend(order = 1),
         lty = guide_legend(order = 2)) +
  theme_paper
p_acc
Warning: Removed 24 row(s) containing missing values (geom_path).

ggsave("../output/acc_by_question_type.pdf", width = 9, height = 3)
Warning: Removed 24 row(s) containing missing values (geom_path).
ggsave("../output/acc_by_question_type.eps", width = 9, height = 3)
Warning: Removed 24 row(s) containing missing values (geom_path).
Warning in grid.Call.graphics(C_rect, x$x, x$y, x$width, x$height,
resolveHJust(x$just, : semi-transparency is not supported on this device:
reported only once per page
ggsave("../output/acc_by_question_type.png", width = 9, height = 3)
Warning: Removed 24 row(s) containing missing values (geom_path).

By level and year

db <- db_connect()
correct_strat <- dbGetQuery(db, 
                      "SELECT r.method AS 'method',
                      r.book_info_id AS 'book_info_id',
                      DATE(r.date + 3600, 'unixepoch') AS 'doy',
                      r.user_id AS 'user',
                      r.choices > 1 AS 'mcq',
                      r.correct AS 'correct',
                      COUNT(*) AS 'n'
                      FROM 'responses_noduplicates' r
                      WHERE r.study == 0
                      GROUP BY r.method,
                      r.book_info_id,
                      DATE(r.date + 3600, 'unixepoch'),
                      r.user_id,
                      r.choices > 1,
                      r.correct"
)
setDT(correct_strat)
db_disconnect(db)

Fill in missing rows (where all trials on a day were correct/incorrect):

correct_strat <- tidyr::complete(correct_strat, tidyr::nesting(method, book_info_id, doy, user, mcq), correct, fill = list(n = 0))
setDT(correct_strat)
correct_strat[, mcq := as.logical(mcq)]

Add book information:

db <- db_connect()
book_info <- dbGetQuery(db, "SELECT DISTINCT * FROM 'book_info'")
db_disconnect(db)

setDT(book_info)
correct_strat[book_info[book_type == "Hoofdboek",], on  = "book_info_id", c("method_group", "book_title") := .(i.method_group, i.book_title)]

Add sensible course names:

correct_strat[, course := ifelse(method == "Grandes Lignes", "French", ifelse(method == "Stepping Stones", "English", "German"))]

Add a school year column (cutoff date: 1 August):

correct_strat[, doy_posix := as.POSIXct(doy)]
correct_strat[, school_year := ifelse(doy_posix < "2019-08-01", "18/19", "19/20")]

Simplify level names:

# Keep all distinctions
correct_strat[, book_title_simple := stringr::str_sub(book_title, 3, -10)]
correct_strat[, book_title_simple := factor(book_title_simple, levels = c("vmbo b/lwoo", "vmbo b", "vmbo bk", "vmbo k", "vmbo kgt", "vmbo-gt", "vmbo gt", "vmbo-gt/havo", "vmbo (t)hv", "havo", "havo vwo", "vwo"))]
# Simplify to three levels
correct_strat[, level := dplyr::case_when(
  grepl( "hv", book_title) ~ "General secondary\n(havo)",
  grepl("vmbo", book_title) ~ "Pre-vocational\n(vmbo)",
  grepl("havo", book_title) ~ "General secondary\n(havo)",
  grepl("vwo", book_title) ~ "Pre-university\n(vwo)",
  TRUE ~ "Other")]
correct_strat[, level := factor(level, levels = c("Other", "Pre-vocational\n(vmbo)", "General secondary\n(havo)", "Pre-university\n(vwo)"))]

Simplify year names:

correct_strat[, year := dplyr::case_when(
  method_group == "Leerjaar 1 (5e Ed.)" ~ "Year 1",
  method_group == "Leerjaar 2 (5e Ed.)" ~ "Year 2",
  method_group == "Leerjaar 3 (5e Ed.)" ~ "Year 3",
  method_group == "Leerjaar 3/4 (5e Ed.)" ~ "Year 3/4",
  method_group == "Leerjaar 4 (5e Ed.)" ~ "Year 4",
  method_group == "Tweede Fase (6e Ed.)" ~ "Tweede Fase",
  TRUE ~ "Other")]

Consolidate by day:

accuracy_strat <- correct_strat[, .(accuracy = n[correct == 1]/sum(n), n = sum(n)), by = .(school_year, doy_posix, course, level, year, user, mcq)]

Align school years:

accuracy_strat[school_year == "18/19", doy_posix_aligned := as.POSIXct(doy_posix + 365*24*60*60, origin = "1970-01-01")]
accuracy_strat[school_year == "19/20", doy_posix_aligned := doy_posix]

Use cut.Date() to bin dates by week. Each day is assigned the date of the most recent Monday.

accuracy_strat[, doy_posix_week := cut.POSIXt(doy_posix, "week")]
accuracy_strat[, doy_posix_aligned_week := cut.POSIXt(doy_posix_aligned, "week")]
accuracy_strat_by_week_and_user <- accuracy_strat[, .(accuracy = sum(accuracy*n)/sum(n)), by = .(course, school_year, doy_posix_aligned_week, level, year, user, mcq)]
accuracy_strat_by_week <- accuracy_strat_by_week_and_user[, .(accuracy_mean = mean(accuracy, na.rm = TRUE),
                              accuracy_se = sd(accuracy, na.rm = TRUE)/sqrt(.N), n = .N), by = .(course, school_year, doy_posix_aligned_week, level, year, mcq)]

Add question type labels:

accuracy_strat_by_week_and_user[, question_type := ifelse(mcq == TRUE, "Multiple\nchoice", "Open\nanswer")]
accuracy_strat_by_week[, question_type := ifelse(mcq == TRUE, "Multiple\nchoice", "Open\nanswer")]

How many unique users per group?

accuracy_strat_by_week_and_user[, .(unique_users = length(unique(user))),  by = .(course, level, year, school_year, question_type)]

Plot response accuracy by week (mean +/- 1 SE).

p_acc_level_year <- ggplot(accuracy_strat_by_week[course == "French" & level != "Other",],
            aes(x = as.POSIXct(doy_posix_aligned_week), y = accuracy_mean, group = interaction(school_year, question_type), colour = school_year, fill = school_year)) +
  facet_grid(level ~ year) +
  geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = 0, ymax = 1.05, fill = "grey92", colour = "grey50", lty = 2, alpha = .9) +
  geom_ribbon(aes(ymin = accuracy_mean - accuracy_se, ymax = accuracy_mean + accuracy_se, colour = NULL), alpha = 0.2) +
  geom_line(aes(lty = question_type)) +
  scale_x_datetime(expand = c(0, 0),
                   breaks = as.POSIXct(c(
                     "2019-10-01 02:00:00 CET",
                     "2019-12-01 02:00:00 CET",
                     "2020-02-01 02:00:00 CET",
                     "2020-04-01 02:00:00 CET",
                     "2020-06-01 02:00:00 CET")),
                   limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
                   date_labels = "%b") +
  scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
  coord_cartesian(ylim = c(.4, 1)) +
  scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
  scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
  labs(x = NULL,
       y = "Accuracy",
       colour = "School year",
       fill = "School year",
       lty = "Question type") +
  guides(colour = guide_legend(order = 1),
         fill = guide_legend(order = 1),
         lty = guide_legend(order = 2)) +
  theme_paper

p_acc_level_year
Warning: Removed 11 row(s) containing missing values (geom_path).

ggsave("../output/acc_by_question_type_french_level_year.pdf", width = 9, height = 5)
Warning: Removed 11 row(s) containing missing values (geom_path).
ggsave("../output/acc_by_question_type_french_level_year.eps", width = 9, height = 5)
Warning: Removed 11 row(s) containing missing values (geom_path).
Warning in grid.Call.graphics(C_rect, x$x, x$y, x$width, x$height,
resolveHJust(x$just, : semi-transparency is not supported on this device:
reported only once per page
ggsave("../output/acc_by_question_type_french_level_year.png", width = 9, height = 5)
Warning: Removed 11 row(s) containing missing values (geom_path).
p_acc_level_year <- ggplot(accuracy_strat_by_week[course == "English" & level != "Other" & question_type == "Multiple\nchoice",],
            aes(x = as.POSIXct(doy_posix_aligned_week), y = accuracy_mean, group = interaction(school_year, question_type), colour = school_year, fill = school_year)) +
  facet_grid(level ~ year) +
  geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = 0, ymax = 1.05, fill = "grey92", colour = "grey50", lty = 2, alpha = .9) +
  geom_ribbon(aes(ymin = accuracy_mean - accuracy_se, ymax = accuracy_mean + accuracy_se, colour = NULL), alpha = 0.2) +
  geom_line() +
  scale_x_datetime(expand = c(0, 0),
                   breaks = as.POSIXct(c(
                     "2019-10-01 02:00:00 CET",
                     "2019-12-01 02:00:00 CET",
                     "2020-02-01 02:00:00 CET",
                     "2020-04-01 02:00:00 CET",
                     "2020-06-01 02:00:00 CET")),
                   limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
                   date_labels = "%b") +
  scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
  coord_cartesian(ylim = c(.4, 1)) +
  scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
  scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
  labs(x = NULL,
       y = "Accuracy",
       colour = "School year",
       fill = "School year",
       lty = "Question type") +
  guides(colour = guide_legend(order = 1),
         fill = guide_legend(order = 1),
         lty = guide_legend(order = 2)) +
  theme_paper

p_acc_level_year
Warning: Removed 5 row(s) containing missing values (geom_path).

ggsave("../output/acc_by_question_type_english_level_year.pdf", width = 9, height = 5)
Warning: Removed 5 row(s) containing missing values (geom_path).
ggsave("../output/acc_by_question_type_english_level_year.eps", width = 9, height = 5)
Warning: Removed 5 row(s) containing missing values (geom_path).
Warning in grid.Call.graphics(C_rect, x$x, x$y, x$width, x$height,
resolveHJust(x$just, : semi-transparency is not supported on this device:
reported only once per page
ggsave("../output/acc_by_question_type_english_level_year.png", width = 9, height = 5)
Warning: Removed 5 row(s) containing missing values (geom_path).

By level

accuracy_level_by_week_and_user <- accuracy_strat[, .(accuracy = sum(accuracy*n)/sum(n)), by = .(course, school_year, doy_posix_aligned_week, level, user, mcq)]

accuracy_level_by_week <- accuracy_level_by_week_and_user[, .(accuracy_mean = mean(accuracy, na.rm = TRUE),
                              accuracy_se = sd(accuracy, na.rm = TRUE)/sqrt(.N), n = .N), by = .(course, school_year, doy_posix_aligned_week, level, mcq)]

Add question type labels:

accuracy_level_by_week[, question_type := ifelse(mcq == TRUE, "Multiple\nchoice", "Open\nanswer")]

How many users in each group?

accuracy_level_by_week_and_user[, .(unique_users = length(unique(user))),  by = .(course, level, school_year, mcq)]

Plot response accuracy by week (mean +/- 1 SE).

p_acc_level <- ggplot(accuracy_level_by_week[((course == "English" & question_type == "Multiple\nchoice") | course == "French") & level != "Other",],
            aes(x = as.POSIXct(doy_posix_aligned_week), y = accuracy_mean, group = interaction(school_year, question_type), colour = school_year, fill = school_year)) +
  facet_grid(level ~ course) +
  geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = 0, ymax = 1.05, fill = "grey92", colour = "grey50", lty = 2, alpha = .9) +
  geom_ribbon(aes(ymin = accuracy_mean - accuracy_se, ymax = accuracy_mean + accuracy_se, colour = NULL), alpha = 0.2) +
  geom_line(aes(lty = question_type)) +
  scale_x_datetime(expand = c(0, 0),
                   breaks = as.POSIXct(c(
                     "2019-10-01 02:00:00 CET",
                     "2019-12-01 02:00:00 CET",
                     "2020-02-01 02:00:00 CET",
                     "2020-04-01 02:00:00 CET",
                     "2020-06-01 02:00:00 CET")),
                   limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
                   date_labels = "%b") +
  scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
  coord_cartesian(ylim = c(.6, 1)) +
  scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
  scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
  labs(x = NULL,
       y = "Accuracy",
       colour = "School year",
       fill = "School year",
       lty = "Question type") +
  guides(colour = guide_legend(order = 1),
         fill = guide_legend(order = 1),
         lty = guide_legend(order = 2)) +
  theme_paper

p_acc_level
Warning: Removed 20 row(s) containing missing values (geom_path).

ggsave("../output/acc_by_question_type_level.pdf", width = 9, height = 5)
Warning: Removed 20 row(s) containing missing values (geom_path).
ggsave("../output/acc_by_question_type_level.eps", width = 9, height = 5)
Warning: Removed 20 row(s) containing missing values (geom_path).
Warning in grid.Call.graphics(C_rect, x$x, x$y, x$width, x$height,
resolveHJust(x$just, : semi-transparency is not supported on this device:
reported only once per page
ggsave("../output/acc_by_question_type_level.png", width = 9, height = 5)
Warning: Removed 20 row(s) containing missing values (geom_path).

By year

accuracy_year_by_week_and_user <- accuracy_strat[, .(accuracy = sum(accuracy*n)/sum(n)), by = .(course, school_year, doy_posix_aligned_week, year, user, mcq)]

accuracy_year_by_week <- accuracy_year_by_week_and_user[, .(accuracy_mean = mean(accuracy, na.rm = TRUE),
                              accuracy_se = sd(accuracy, na.rm = TRUE)/sqrt(.N), n = .N), by = .(course, school_year, doy_posix_aligned_week, year, mcq)]

Add question type labels:

accuracy_year_by_week[, question_type := ifelse(mcq == TRUE, "Multiple\nchoice", "Open\nanswer")]

How many users in each group?

accuracy_year_by_week_and_user[, .(unique_users = length(unique(user))),  by = .(course, year, school_year, mcq)]

Plot response accuracy by week (mean +/- 1 SE).

p_acc_year <- ggplot(accuracy_year_by_week[((course == "English" & question_type == "Multiple\nchoice") | course == "French") & year != "Other",],
            aes(x = as.POSIXct(doy_posix_aligned_week), y = accuracy_mean, group = interaction(school_year, question_type), colour = school_year, fill = school_year)) +
  facet_grid(year ~ course) +
  geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = 0, ymax = 1.05, fill = "grey92", colour = "grey50", lty = 2, alpha = .9) +
  geom_ribbon(aes(ymin = accuracy_mean - accuracy_se, ymax = accuracy_mean + accuracy_se, colour = NULL), alpha = 0.2) +
  geom_line(aes(lty = question_type)) +
  scale_x_datetime(expand = c(0, 0),
                   breaks = as.POSIXct(c(
                     "2019-10-01 02:00:00 CET",
                     "2019-12-01 02:00:00 CET",
                     "2020-02-01 02:00:00 CET",
                     "2020-04-01 02:00:00 CET",
                     "2020-06-01 02:00:00 CET")),
                   limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
                   date_labels = "%b") +
  scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
  coord_cartesian(ylim = c(.6, 1)) +
  scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
  scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
  labs(x = NULL,
       y = "Accuracy",
       colour = "School year",
       fill = "School year",
       lty = "Question type") +
  guides(colour = guide_legend(order = 1),
         fill = guide_legend(order = 1),
         lty = guide_legend(order = 2)) +
  theme_paper

p_acc_year
Warning: Removed 14 row(s) containing missing values (geom_path).

ggsave("../output/acc_by_question_type_year.pdf", width = 9, height = 5)
Warning: Removed 14 row(s) containing missing values (geom_path).
ggsave("../output/acc_by_question_type_year.eps", width = 9, height = 5)
Warning: Removed 14 row(s) containing missing values (geom_path).
Warning in grid.Call.graphics(C_rect, x$x, x$y, x$width, x$height,
resolveHJust(x$just, : semi-transparency is not supported on this device:
reported only once per page
ggsave("../output/acc_by_question_type_year.png", width = 9, height = 5)
Warning: Removed 14 row(s) containing missing values (geom_path).

Regression model

Fit a mixed effects model to the daily accuracy data:

accuracy[, period := dplyr::case_when(
  doy_posix_aligned >= date_schools_opened ~ "post-lockdown",
  doy_posix_aligned >= date_schools_closed & doy_posix_aligned < date_schools_opened ~ "during-lockdown",
  doy_posix_aligned < date_schools_opened ~ "pre-lockdown"
)]
# Reorder factor levels so that intercept is pre-lockdown open answer in 19/20
accuracy[, period := factor(period, levels = c("pre-lockdown", "during-lockdown", "post-lockdown"))]
accuracy[, school_year := factor(school_year, levels = c("19/20", "18/19"))]
accuracy[, mcq := factor(mcq, levels = c(FALSE, TRUE))]

Since we know the number of trials per day and the proportion correct (accuracy), we can use a binomial GLMM:

if(!file.exists("../output/m_acc_fit.rds")) {
  m_acc <- glmer(accuracy ~ period*school_year*mcq + (1 | user) + (1 | course),
                 data = accuracy[(course == "English" & mcq == TRUE) | course == "French",],
                 family = "binomial", 
                 weights = n,
                 nAGQ = 0,
                 control = glmerControl(optimizer = "bobyqa", optCtrl = list(maxfun = 1e6)))
  saveRDS(m_acc, "../output/m_acc_fit.rds")
} else {
  m_acc <- readRDS("../output/m_acc_fit.rds")
}

m_acc_summary <- summary(m_acc)
m_acc_summary
Generalized linear mixed model fit by maximum likelihood (Adaptive
  Gauss-Hermite Quadrature, nAGQ = 0) [glmerMod]
 Family: binomial  ( logit )
Formula: accuracy ~ period * school_year * mcq + (1 | user) + (1 | course)
   Data: 
accuracy[(course == "English" & mcq == TRUE) | course == "French",      ]
Weights: n
Control: 
glmerControl(optimizer = "bobyqa", optCtrl = list(maxfun = 1e+06))

     AIC      BIC   logLik deviance df.resid 
 4963073  4963232 -2481522  4963045   671406 

Scaled residuals: 
     Min       1Q   Median       3Q      Max 
-23.9963  -0.9227   0.1606   1.0936  11.4371 

Random effects:
 Groups Name        Variance Std.Dev.
 user   (Intercept) 0.35034  0.5919  
 course (Intercept) 0.05511  0.2348  
Number of obs: 671420, groups:  user, 133419; course, 2

Fixed effects:
                                                Estimate Std. Error
(Intercept)                                     1.631257   0.166016
periodduring-lockdown                           0.259227   0.002725
periodpost-lockdown                             0.150384   0.004021
school_year18/19                                0.117385   0.002566
mcqTRUE                                         0.960249   0.002088
periodduring-lockdown:school_year18/19         -0.311338   0.004409
periodpost-lockdown:school_year18/19           -0.242816   0.006325
periodduring-lockdown:mcqTRUE                  -0.328352   0.002881
periodpost-lockdown:mcqTRUE                    -0.277485   0.004397
school_year18/19:mcqTRUE                        0.003357   0.002667
periodduring-lockdown:school_year18/19:mcqTRUE  0.379504   0.004758
periodpost-lockdown:school_year18/19:mcqTRUE    0.335859   0.006970
                                                z value Pr(>|z|)    
(Intercept)                                       9.826   <2e-16 ***
periodduring-lockdown                            95.142   <2e-16 ***
periodpost-lockdown                              37.399   <2e-16 ***
school_year18/19                                 45.755   <2e-16 ***
mcqTRUE                                         459.903   <2e-16 ***
periodduring-lockdown:school_year18/19          -70.613   <2e-16 ***
periodpost-lockdown:school_year18/19            -38.387   <2e-16 ***
periodduring-lockdown:mcqTRUE                  -113.954   <2e-16 ***
periodpost-lockdown:mcqTRUE                     -63.110   <2e-16 ***
school_year18/19:mcqTRUE                          1.259    0.208    
periodduring-lockdown:school_year18/19:mcqTRUE   79.765   <2e-16 ***
periodpost-lockdown:school_year18/19:mcqTRUE     48.183   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Correlation of Fixed Effects:
                  (Intr) prddr- prdps- sc_18/19 mcTRUE prdd-:_18/19
prddrng-lck       -0.007                                           
prdpst-lckd       -0.005  0.346                                    
schl_y18/19       -0.008  0.452  0.297                             
mcqTRUE           -0.010  0.534  0.352  0.592                      
prdd-:_18/19       0.005 -0.626 -0.217 -0.505   -0.340             
prdp-:_18/19       0.003 -0.229 -0.640 -0.351   -0.233  0.241      
prddr-:TRUE        0.007 -0.824 -0.278 -0.412   -0.649  0.516      
prdps-:TRUE        0.004 -0.269 -0.824 -0.264   -0.419  0.169      
s_18/19:TRU        0.007 -0.409 -0.270 -0.837   -0.697  0.450      
prdd-:_18/19:TRUE -0.004  0.506  0.171  0.439    0.405 -0.854      
prdp-:_18/19:TRUE -0.003  0.176  0.523  0.298    0.273 -0.190      
                  prdp-:_18/19 prdd-:TRUE prdp-:TRUE s_18/19:
prddrng-lck                                                  
prdpst-lckd                                                  
schl_y18/19                                                  
mcqTRUE                                                      
prdd-:_18/19                                                 
prdp-:_18/19                                                 
prddr-:TRUE        0.183                                     
prdps-:TRUE        0.527        0.315                        
s_18/19:TRU        0.311        0.481      0.310             
prdd-:_18/19:TRUE -0.194       -0.612     -0.194     -0.523  
prdp-:_18/19:TRUE -0.843       -0.205     -0.634     -0.358  
                  prdd-:_18/19:TRUE
prddrng-lck                        
prdpst-lckd                        
schl_y18/19                        
mcqTRUE                            
prdd-:_18/19                       
prdp-:_18/19                       
prddr-:TRUE                        
prdps-:TRUE                        
s_18/19:TRU                        
prdd-:_18/19:TRUE                  
prdp-:_18/19:TRUE  0.221           

Save coefficients as a table for in the paper:

m_acc_coef <- as.data.frame(m_acc_summary$coefficients)
setDT(m_acc_coef, keep.rownames = TRUE)
m_acc_coef$rn <- c("Intercept \\small{(Period: pre-lockdown, School year: 19/20, Question type: open answer)}",
                   "Period: lockdown",
                   "Period: post-lockdown",
                   "School year: 18/19",
                   "Question type: multiple choice",
                   "Period: lockdown $\\times$ School year: 18/19",
                   "Period: post-lockdown $\\times$ School year: 18/19",
                   "Period: lockdown $\\times$ Question type: multiple choice",
                   "Period: post-lockdown $\\times$ Question type: multiple choice",
                   "School year: 18/19 $\\times$ Question type: multiple choice",
                   "Period: lockdown $\\times$ School year: 18/19 $\\times$ Question type: multiple choice",
                   "Period: post-lockdown $\\times$ School year: 18/19 $\\times$ Question type: multiple choice")

# Format p-values
m_acc_coef$`Pr(>|z|)` <- format.pval(m_acc_coef$`Pr(>|z|)`, eps = .001, digits = 3, flag = "0")
m_acc_coef$`Pr(>|z|)` <- sub('^(<)?0[.]', '\\1.', m_acc_coef$`Pr(>|z|)`) # Remove leading zero

cat(knitr::kable(m_acc_coef,
                 align = c("l","r", "r", "r", "r"),
                 digits = c(NA, 3, 3, 2, NA),
                 col.names = c("Effect", "$b$", "SE", "$z$", "$p$"),
                 format = "latex",
                 booktabs = TRUE,
                 escape = FALSE),
    file = "../output/m_acc_table.tex")

Visualise the model fit:

acc_fit <- expand.grid(period = c("pre-lockdown", "during-lockdown", "post-lockdown"), school_year = c("18/19", "19/20"), mcq = c(TRUE, FALSE))
acc_fit <- cbind(acc_fit, accuracy = predict(m_acc, type = "response", re.form = NA, newdata = acc_fit))
acc_fit
ggplot(acc_fit, aes(x = period, y = accuracy, colour = school_year, lty = mcq, group = interaction(mcq, school_year))) +
  geom_line() +
  geom_point() +
  scale_y_continuous(limits = c(.7, 1), labels = scales::percent_format()) +
  theme_paper

Empirical means:

accuracy_mean <- accuracy[(course == "English" & mcq == TRUE) | course == "French", .(accuracy = sum(accuracy * n)/sum(n)), by = .(period, school_year, mcq, user, course)][, .(accuracy = mean(accuracy), accuracy_sd = sd(accuracy)), by = .(period, school_year, mcq)]
accuracy_mean
ggplot(accuracy_mean, aes(x = period, y = accuracy, colour = school_year, lty = mcq, group = interaction(mcq, school_year))) +
  geom_line() +
  geom_point() +
  scale_y_continuous(limits = c(.7, 1), labels = scales::percent_format()) +
  theme_paper

Response time

db <- db_connect()
rt <- dbGetQuery(db, 
                 "SELECT r.method AS 'method',
                  r.book_info_id AS 'book_info_id',
                  DATE(r.date + 3600, 'unixepoch') AS 'doy',
                  r.user_id AS 'user',
                  r.choices > 1 AS 'mcq',
                  r.rt AS 'rt'
                  FROM 'responses_noduplicates' r
                  WHERE r.study == 0
                  AND r.correct == 1
                 "
)
setDT(rt)
db_disconnect(db)
doys <- rt[, .(doy = unique(doy))][, doy_posix := as.POSIXct(doy)][]
doys[, doy_posix_week := cut.POSIXt(as.POSIXct(doy), "week")]
doys[, school_year := ifelse(doy_posix < "2019-08-01", "18/19", "19/20")]
doys[school_year == "18/19", doy_posix_aligned := as.POSIXct(doy_posix + 365*24*60*60, origin = "1970-01-01")]
doys[school_year == "19/20", doy_posix_aligned := doy_posix]
doys[, doy_posix_aligned_week := cut.POSIXt(doy_posix_aligned, "week")]
doys[, period := dplyr::case_when(
  doy_posix_aligned >= date_schools_opened ~ "post-lockdown",
  doy_posix_aligned >= date_schools_closed & doy_posix_aligned < date_schools_opened ~ "during-lockdown",
  doy_posix_aligned < date_schools_opened ~ "pre-lockdown"
)]
# Reorder factor levels so that intercept is pre-lockdown in 19/20
doys[, period := factor(period, levels = c("pre-lockdown", "during-lockdown", "post-lockdown"))]
doys[, school_year := factor(school_year, levels = c("19/20", "18/19"))]
rt <- rt[doys, on = "doy"]
rt[, mcq := as.factor(as.logical(mcq))]
rt[, course := ifelse(method == "Grandes Lignes", "French", ifelse(method == "Stepping Stones", "English", "German"))]

Throw out trials with negative RTs (timing errors)

rt <- rt[rt > 0]

Whole population

rt_med <- rt[, .(rt_median = median(rt)), by = .(school_year, mcq, user, course, doy_posix_week)]

rt_by_week <- rt_med[, .(rt = mean(rt_median), rt_se = sd(rt_median)/sqrt(.N)), by = .(school_year, mcq, course, doy_posix_week)]

Overlap the two school years:

rt_by_week[school_year == "18/19", doy_posix_week_aligned := as.POSIXct(as.POSIXct(doy_posix_week) + 365*24*60*60, origin = "1970-01-01")]
rt_by_week[school_year == "19/20", doy_posix_week_aligned := as.POSIXct(doy_posix_week)]

Add question type labels:

rt_by_week[, question_type := ifelse(mcq == TRUE, "Multiple\nchoice", "Open\nanswer")]
rt_by_week[, school_year := factor(school_year, levels = c("18/19", "19/20"))]

Plot response time by week (mean +/- 1 SE).

p_rt <- ggplot(rt_by_week[(course == "English" & mcq == TRUE) | course == "French",],
            aes(x = doy_posix_week_aligned, y = rt/1e3, group = interaction(school_year, question_type), colour = school_year, fill = school_year)) +
  facet_grid(. ~ course) +
  geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = 0, ymax = 1000, fill = "grey92", colour = "grey50", lty = 2, alpha = .9) +
  geom_ribbon(aes(ymin = rt/1e3 - rt_se/1e3, ymax = rt/1e3 + rt_se/1e3, colour = NULL), alpha = 0.2) +
  geom_line(aes(lty = question_type)) +
  scale_x_datetime(expand = c(0, 0),
                   breaks = as.POSIXct(c(
                     "2019-10-01 02:00:00 CET",
                     "2019-12-01 02:00:00 CET",
                     "2020-02-01 02:00:00 CET",
                     "2020-04-01 02:00:00 CET",
                     "2020-06-01 02:00:00 CET")),
                   limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
                   date_labels = "%b") +
  scale_y_continuous(labels = scales::unit_format(unit = "s", accuracy = .1)) +
  coord_cartesian(ylim = c(1.7, 3.7)) +
  scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
  scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
  labs(x = NULL,
       y = "Response time",
       colour = "School year",
       fill = "School year",
       lty = "Question type") +
  guides(colour = guide_legend(order = 1),
         fill = guide_legend(order = 1),
         lty = guide_legend(order = 2)) +
  theme_paper

p_rt
Warning: Removed 24 row(s) containing missing values (geom_path).

ggsave("../output/rt_by_question_type.pdf", width = 9, height = 3)
Warning: Removed 24 row(s) containing missing values (geom_path).
ggsave("../output/rt_by_question_type.eps", width = 9, height = 3)
Warning: Removed 24 row(s) containing missing values (geom_path).
Warning in grid.Call.graphics(C_rect, x$x, x$y, x$width, x$height,
resolveHJust(x$just, : semi-transparency is not supported on this device:
reported only once per page
ggsave("../output/rt_by_question_type.png", width = 9, height = 3)
Warning: Removed 24 row(s) containing missing values (geom_path).

By level and year

Add book info:

rt[book_info[book_type == "Hoofdboek",], on  = "book_info_id", c("method_group", "book_title") := .(i.method_group, i.book_title)]

Simplify level names:

# Keep all distinctions
rt[, book_title_simple := stringr::str_sub(book_title, 3, -10)]
rt[, book_title_simple := factor(book_title_simple, levels = c("vmbo b/lwoo", "vmbo b", "vmbo bk", "vmbo k", "vmbo kgt", "vmbo-gt", "vmbo gt", "vmbo-gt/havo", "vmbo (t)hv", "havo", "havo vwo", "vwo"))]
# Simplify to three levels
rt[, level := dplyr::case_when(
  grepl( "hv", book_title) ~ "General secondary\n(havo)",
  grepl("vmbo", book_title) ~ "Pre-vocational\n(vmbo)",
  grepl("havo", book_title) ~ "General secondary\n(havo)",
  grepl("vwo", book_title) ~ "Pre-university\n(vwo)",
  TRUE ~ "Other")]
rt[, level := factor(level, levels = c("Other", "Pre-vocational\n(vmbo)", "General secondary\n(havo)", "Pre-university\n(vwo)"))]

Simplify year names:

rt[, year := dplyr::case_when(
  method_group == "Leerjaar 1 (5e Ed.)" ~ "Year 1",
  method_group == "Leerjaar 2 (5e Ed.)" ~ "Year 2",
  method_group == "Leerjaar 3 (5e Ed.)" ~ "Year 3",
  method_group == "Leerjaar 3/4 (5e Ed.)" ~ "Year 3/4",
  method_group == "Leerjaar 4 (5e Ed.)" ~ "Year 4",
  method_group == "Tweede Fase (6e Ed.)" ~ "Tweede Fase",
  TRUE ~ "Other")]
rt_strat_med <- rt[, .(rt_median = median(rt)), by = .(school_year, mcq, user, course, level, year, doy_posix_week)]

rt_strat_by_week <- rt_strat_med[, .(rt = mean(rt_median), rt_se = sd(rt_median)/sqrt(.N)), by = .(school_year, mcq, course, level, year, doy_posix_week)]

Overlap the two school years:

rt_strat_by_week[school_year == "18/19", doy_posix_week_aligned := as.POSIXct(as.POSIXct(doy_posix_week) + 365*24*60*60, origin = "1970-01-01")]
rt_strat_by_week[school_year == "19/20", doy_posix_week_aligned := as.POSIXct(doy_posix_week)]

Add question type labels:

rt_strat_by_week[, question_type := ifelse(mcq == TRUE, "Multiple\nchoice", "Open\nanswer")]
rt_strat_by_week[, school_year := factor(school_year, levels = c("18/19", "19/20"))]

Plot response time by week (mean +/- 1 SE).

p_rt_level_year <- ggplot(rt_strat_by_week[course == "French",],
            aes(x = doy_posix_week_aligned, y = rt/1e3, group = interaction(school_year, question_type), colour = school_year, fill = school_year)) +
  facet_grid(level ~ year) +
  geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = 0, ymax = 1000, fill = "grey92", colour = "grey50", lty = 2, alpha = .9) +
  geom_ribbon(aes(ymin = rt/1e3 - rt_se/1e3, ymax = rt/1e3 + rt_se/1e3, colour = NULL), alpha = 0.2) +
  geom_line(aes(lty = question_type)) +
  scale_x_datetime(expand = c(0, 0),
                   breaks = as.POSIXct(c(
                     "2019-10-01 02:00:00 CET",
                     "2019-12-01 02:00:00 CET",
                     "2020-02-01 02:00:00 CET",
                     "2020-04-01 02:00:00 CET",
                     "2020-06-01 02:00:00 CET")),
                   limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
                   date_labels = "%b") +
  scale_y_continuous(labels = scales::unit_format(unit = "s", accuracy = .1)) +
  coord_cartesian(ylim = c(1, 4)) +
  scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
  scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
  labs(x = NULL,
       y = "Response time",
       colour = "School year",
       fill = "School year",
       lty = "Question type") +
  guides(colour = guide_legend(order = 1),
         fill = guide_legend(order = 1),
         lty = guide_legend(order = 2)) +
  theme_paper

p_rt_level_year
Warning: Removed 11 row(s) containing missing values (geom_path).

ggsave("../output/rt_by_question_type_french_level_year.pdf", width = 9, height = 3)
Warning: Removed 11 row(s) containing missing values (geom_path).
ggsave("../output/rt_by_question_type_french_level_year.eps", width = 9, height = 3)
Warning: Removed 11 row(s) containing missing values (geom_path).
Warning in grid.Call.graphics(C_rect, x$x, x$y, x$width, x$height,
resolveHJust(x$just, : semi-transparency is not supported on this device:
reported only once per page
ggsave("../output/rt_by_question_type_french_level_year.png", width = 9, height = 3)
Warning: Removed 11 row(s) containing missing values (geom_path).
p_rt_level_year <- ggplot(rt_strat_by_week[course == "English" & question_type == "Multiple\nchoice" & level != "Other",],
            aes(x = doy_posix_week_aligned, y = rt/1e3, group = interaction(school_year, question_type), colour = school_year, fill = school_year)) +
  facet_grid(level ~ year) +
  geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = 0, ymax = 1000, fill = "grey92", colour = "grey50", lty = 2, alpha = .9) +
  geom_ribbon(aes(ymin = rt/1e3 - rt_se/1e3, ymax = rt/1e3 + rt_se/1e3, colour = NULL), alpha = 0.2) +
  geom_line(aes(lty = question_type)) +
  scale_x_datetime(expand = c(0, 0),
                   breaks = as.POSIXct(c(
                     "2019-10-01 02:00:00 CET",
                     "2019-12-01 02:00:00 CET",
                     "2020-02-01 02:00:00 CET",
                     "2020-04-01 02:00:00 CET",
                     "2020-06-01 02:00:00 CET")),
                   limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
                   date_labels = "%b") +
  scale_y_continuous(labels = scales::unit_format(unit = "s", accuracy = .1)) +
  coord_cartesian(ylim = c(1, 4)) +
  scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
  scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
  labs(x = NULL,
       y = "Response time",
       colour = "School year",
       fill = "School year",
       lty = "Question type") +
  guides(colour = guide_legend(order = 1),
         fill = guide_legend(order = 1),
         lty = guide_legend(order = 2)) +
  theme_paper

p_rt_level_year
Warning: Removed 5 row(s) containing missing values (geom_path).

ggsave("../output/rt_by_question_type_english_level_year.pdf", width = 9, height = 3)
Warning: Removed 5 row(s) containing missing values (geom_path).
ggsave("../output/rt_by_question_type_english_level_year.eps", width = 9, height = 3)
Warning: Removed 5 row(s) containing missing values (geom_path).
Warning in grid.Call.graphics(C_rect, x$x, x$y, x$width, x$height,
resolveHJust(x$just, : semi-transparency is not supported on this device:
reported only once per page
ggsave("../output/rt_by_question_type_english_level_year.png", width = 9, height = 3)
Warning: Removed 5 row(s) containing missing values (geom_path).

By level

rt_level_med <- rt[, .(rt_median = median(rt)), by = .(school_year, mcq, user, course, level, doy_posix_week)]

rt_level_by_week <- rt_level_med[, .(rt = mean(rt_median), rt_se = sd(rt_median)/sqrt(.N)), by = .(school_year, mcq, course, level, doy_posix_week)]

Overlap the two school years:

rt_level_by_week[school_year == "18/19", doy_posix_week_aligned := as.POSIXct(as.POSIXct(doy_posix_week) + 365*24*60*60, origin = "1970-01-01")]
rt_level_by_week[school_year == "19/20", doy_posix_week_aligned := as.POSIXct(doy_posix_week)]

Add question type labels:

rt_level_by_week[, question_type := ifelse(mcq == TRUE, "Multiple\nchoice", "Open\nanswer")]
rt_level_by_week[, school_year := factor(school_year, levels = c("18/19", "19/20"))]

Plot response time by week (mean +/- 1 SE).

p_rt_level <- ggplot(rt_level_by_week[((course == "English" & question_type == "Multiple\nchoice") | course == "French") & level != "Other",],
            aes(x = doy_posix_week_aligned, y = rt/1e3, group = interaction(school_year, question_type), colour = school_year, fill = school_year)) +
  facet_grid(level ~ course) +
  geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = 0, ymax = 1000, fill = "grey92", colour = "grey50", lty = 2, alpha = .9) +
  geom_ribbon(aes(ymin = rt/1e3 - rt_se/1e3, ymax = rt/1e3 + rt_se/1e3, colour = NULL), alpha = 0.2) +
  geom_line(aes(lty = question_type)) +
  scale_x_datetime(expand = c(0, 0),
                   breaks = as.POSIXct(c(
                     "2019-10-01 02:00:00 CET",
                     "2019-12-01 02:00:00 CET",
                     "2020-02-01 02:00:00 CET",
                     "2020-04-01 02:00:00 CET",
                     "2020-06-01 02:00:00 CET")),
                   limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
                   date_labels = "%b") +
  scale_y_continuous(labels = scales::unit_format(unit = "s", accuracy = .1)) +
  coord_cartesian(ylim = c(1, 6)) +
  scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
  scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
  labs(x = NULL,
       y = "Response time",
       colour = "School year",
       fill = "School year",
       lty = "Question type") +
  guides(colour = guide_legend(order = 1),
         fill = guide_legend(order = 1),
         lty = guide_legend(order = 2)) +
  theme_paper

p_rt_level
Warning: Removed 20 row(s) containing missing values (geom_path).

ggsave("../output/rt_by_question_type_level.pdf", width = 9, height = 5)
Warning: Removed 20 row(s) containing missing values (geom_path).
ggsave("../output/rt_by_question_type_level.eps", width = 9, height = 5)
Warning: Removed 20 row(s) containing missing values (geom_path).
Warning in grid.Call.graphics(C_rect, x$x, x$y, x$width, x$height,
resolveHJust(x$just, : semi-transparency is not supported on this device:
reported only once per page
ggsave("../output/rt_by_question_type_level.png", width = 9, height = 5)
Warning: Removed 20 row(s) containing missing values (geom_path).

By year

rt_year_med <- rt[, .(rt_median = median(rt)), by = .(school_year, mcq, user, course, year, doy_posix_week)]

rt_year_by_week <- rt_year_med[, .(rt = mean(rt_median), rt_se = sd(rt_median)/sqrt(.N)), by = .(school_year, mcq, course, year, doy_posix_week)]

Overlap the two school years:

rt_year_by_week[school_year == "18/19", doy_posix_week_aligned := as.POSIXct(as.POSIXct(doy_posix_week) + 365*24*60*60, origin = "1970-01-01")]
rt_year_by_week[school_year == "19/20", doy_posix_week_aligned := as.POSIXct(doy_posix_week)]

Add question type labels:

rt_year_by_week[, question_type := ifelse(mcq == TRUE, "Multiple\nchoice", "Open\nanswer")]
rt_year_by_week[, school_year := factor(school_year, levels = c("18/19", "19/20"))]

Plot response time by week (mean +/- 1 SE).

p_rt_year <- ggplot(rt_year_by_week[((course == "English" & question_type == "Multiple\nchoice") | course == "French") & year != "Other",],
            aes(x = doy_posix_week_aligned, y = rt/1e3, group = interaction(school_year, question_type), colour = school_year, fill = school_year)) +
  facet_grid(year ~ course) +
  geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = 0, ymax = 1000, fill = "grey92", colour = "grey50", lty = 2, alpha = .9) +
  geom_ribbon(aes(ymin = rt/1e3 - rt_se/1e3, ymax = rt/1e3 + rt_se/1e3, colour = NULL), alpha = 0.2) +
  geom_line(aes(lty = question_type)) +
  scale_x_datetime(expand = c(0, 0),
                   breaks = as.POSIXct(c(
                     "2019-10-01 02:00:00 CET",
                     "2019-12-01 02:00:00 CET",
                     "2020-02-01 02:00:00 CET",
                     "2020-04-01 02:00:00 CET",
                     "2020-06-01 02:00:00 CET")),
                   limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
                   date_labels = "%b") +
  scale_y_continuous(labels = scales::unit_format(unit = "s", accuracy = .1)) +
  coord_cartesian(ylim = c(1, 4)) +
  scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
  scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
  labs(x = NULL,
       y = "Response time",
       colour = "School year",
       fill = "School year",
       lty = "Question type") +
  guides(colour = guide_legend(order = 1),
         fill = guide_legend(order = 1),
         lty = guide_legend(order = 2)) +
  theme_paper

p_rt_year
Warning: Removed 14 row(s) containing missing values (geom_path).

ggsave("../output/rt_by_question_type_year.pdf", width = 9, height = 5)
Warning: Removed 14 row(s) containing missing values (geom_path).
ggsave("../output/rt_by_question_type_year.eps", width = 9, height = 5)
Warning: Removed 14 row(s) containing missing values (geom_path).
Warning in grid.Call.graphics(C_rect, x$x, x$y, x$width, x$height,
resolveHJust(x$just, : semi-transparency is not supported on this device:
reported only once per page
ggsave("../output/rt_by_question_type_year.png", width = 9, height = 5)
Warning: Removed 14 row(s) containing missing values (geom_path).

Regression model

rt_model_dat <- rt[, .(rt_median = median(rt)), by = .(course, school_year, period, doy_posix, mcq, user)]

Fit a generalised linear mixed effects model (assuming a Gamma distribution for RT and an identity link function; Lo & Andrew, 2015) to the daily median RT:

if(!file.exists("../output/m_rt_fit.rds")) {
  m_rt <- glmer(rt_median ~ period*school_year*mcq + (1 | user) + (1 | course),
                 data = rt_model_dat[(course == "English" & mcq == TRUE) | course == "French",],
                 family = Gamma(link = "identity"),
                 nAGQ = 0,
                 control = glmerControl(optimizer = "bobyqa", optCtrl = list(maxfun = 1e6)))
  saveRDS(m_rt, "../output/m_rt_fit.rds")
} else {
  m_rt <- readRDS("../output/m_rt_fit.rds")
}

m_rt_summary <- summary(m_rt)
m_rt_summary
Generalized linear mixed model fit by maximum likelihood (Adaptive
  Gauss-Hermite Quadrature, nAGQ = 0) [glmerMod]
 Family: Gamma  ( identity )
Formula: 
rt_median ~ period * school_year * mcq + (1 | user) + (1 | course)
   Data: rt_model_dat[(course == "English" & mcq == TRUE) | course ==  
    "French", ]
Control: 
glmerControl(optimizer = "bobyqa", optCtrl = list(maxfun = 1e+06))

     AIC      BIC   logLik deviance df.resid 
10332190 10332361 -5166080 10332160   669366 

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
 -2.732  -0.345  -0.070   0.241 220.533 

Random effects:
 Groups   Name        Variance  Std.Dev.
 user     (Intercept) 2.828e+05 531.8041
 course   (Intercept) 2.852e+02  16.8883
 Residual             1.324e-01   0.3639
Number of obs: 669381, groups:  user, 133398; course, 2

Fixed effects:
                                               Estimate Std. Error t value
(Intercept)                                    2133.210     13.019 163.848
periodduring-lockdown                           207.475      6.937  29.909
periodpost-lockdown                             198.282     10.619  18.673
school_year18/19                                 28.172      6.564   4.292
mcqTRUE                                         150.852      5.382  28.028
periodduring-lockdown:school_year18/19         -280.435     11.362 -24.681
periodpost-lockdown:school_year18/19           -294.190     17.057 -17.247
periodduring-lockdown:mcqTRUE                  -210.118      7.464 -28.151
periodpost-lockdown:mcqTRUE                    -199.308     11.721 -17.005
school_year18/19:mcqTRUE                        -62.059      6.894  -9.002
periodduring-lockdown:school_year18/19:mcqTRUE  278.331     12.257  22.708
periodpost-lockdown:school_year18/19:mcqTRUE    315.189     18.735  16.824
                                               Pr(>|z|)    
(Intercept)                                     < 2e-16 ***
periodduring-lockdown                           < 2e-16 ***
periodpost-lockdown                             < 2e-16 ***
school_year18/19                               1.77e-05 ***
mcqTRUE                                         < 2e-16 ***
periodduring-lockdown:school_year18/19          < 2e-16 ***
periodpost-lockdown:school_year18/19            < 2e-16 ***
periodduring-lockdown:mcqTRUE                   < 2e-16 ***
periodpost-lockdown:mcqTRUE                     < 2e-16 ***
school_year18/19:mcqTRUE                        < 2e-16 ***
periodduring-lockdown:school_year18/19:mcqTRUE  < 2e-16 ***
periodpost-lockdown:school_year18/19:mcqTRUE    < 2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Correlation of Fixed Effects:
                  (Intr) prddr- prdps- sc_18/19 mcTRUE prdd-:_18/19
prddrng-lck       -0.251                                           
prdpst-lckd       -0.161  0.334                                    
schl_y18/19       -0.277  0.483  0.311                             
mcqTRUE           -0.335  0.567  0.368  0.619                      
prdd-:_18/19       0.159 -0.617 -0.207 -0.533   -0.352             
prdp-:_18/19       0.105 -0.214 -0.626 -0.356   -0.234  0.229      
prddr-:TRUE        0.226 -0.861 -0.281 -0.434   -0.651  0.530      
prdps-:TRUE        0.143 -0.275 -0.854 -0.275   -0.413  0.170      
s_18/19:TRU        0.245 -0.440 -0.284 -0.879   -0.703  0.483      
prdd-:_18/19:TRUE -0.141  0.529  0.173  0.474    0.403 -0.882      
prdp-:_18/19:TRUE -0.092  0.176  0.537  0.311    0.263 -0.191      
                  prdp-:_18/19 prdd-:TRUE prdp-:TRUE s_18/19:
prddrng-lck                                                  
prdpst-lckd                                                  
schl_y18/19                                                  
mcqTRUE                                                      
prdd-:_18/19                                                 
prdp-:_18/19                                                 
prddr-:TRUE        0.179                                     
prdps-:TRUE        0.534        0.305                        
s_18/19:TRU        0.322        0.490      0.310             
prdd-:_18/19:TRUE -0.195       -0.613     -0.187     -0.539  
prdp-:_18/19:TRUE -0.872       -0.194     -0.627     -0.354  
                  prdd-:_18/19:TRUE
prddrng-lck                        
prdpst-lckd                        
schl_y18/19                        
mcqTRUE                            
prdd-:_18/19                       
prdp-:_18/19                       
prddr-:TRUE                        
prdps-:TRUE                        
s_18/19:TRU                        
prdd-:_18/19:TRUE                  
prdp-:_18/19:TRUE  0.212           

Save coefficients as a table for in the paper:

m_rt_coef <- as.data.frame(m_rt_summary$coefficients)
setDT(m_rt_coef, keep.rownames = TRUE)
m_rt_coef$rn <- c("Intercept \\small{(Period: pre-lockdown, School year: 19/20, Question type: open answer)}",
                   "Period: lockdown",
                   "Period: post-lockdown",
                   "School year: 18/19",
                   "Question type: multiple choice",
                   "Period: lockdown $\\times$ School year: 18/19",
                   "Period: post-lockdown $\\times$ School year: 18/19",
                   "Period: lockdown $\\times$ Question type: multiple choice",
                   "Period: post-lockdown $\\times$ Question type: multiple choice",
                   "School year: 18/19 $\\times$ Question type: multiple choice",
                   "Period: lockdown $\\times$ School year: 18/19 $\\times$ Question type: multiple choice",
                   "Period: post-lockdown $\\times$ School year: 18/19 $\\times$ Question type: multiple choice")

# Format p-values
m_rt_coef$`Pr(>|z|)` <- format.pval(m_rt_coef$`Pr(>|z|)`, eps = .001, digits = 3, flag = "0")
m_rt_coef$`Pr(>|z|)` <- sub('^(<)?0[.]', '\\1.', m_rt_coef$`Pr(>|z|)`) # Remove leading zero

cat(knitr::kable(m_rt_coef,
                 align = c("l","r", "r", "r", "r"),
                 digits = c(NA, 3, 3, 2, NA),
                 col.names = c("Effect", "$b$", "SE", "$z$", "$p$"),
                 format = "latex",
                 booktabs = TRUE,
                 escape = FALSE),
    file = "../output/m_rt_table.tex")

Visualise the model fit:

rt_fit <- expand.grid(period = c("pre-lockdown", "during-lockdown", "post-lockdown"), school_year = c("18/19", "19/20"), mcq = c(TRUE, FALSE))
rt_fit <- cbind(rt_fit, rt = predict(m_rt, type = "response", re.form = NA, newdata = rt_fit))
rt_fit
ggplot(rt_fit, aes(x = period, y = rt, colour = school_year, lty = mcq, group = interaction(mcq, school_year))) +
  geom_line() +
  geom_point() +
  scale_y_continuous(limits = c(1500, 4000)) +
  theme_paper

Empirical means:

rt_mean <- rt_model_dat[(course == "English" & mcq == TRUE) | course == "French", .(rt = mean(rt_median)), by = .(period, school_year, mcq, user, course)][, .(rt = mean(rt), rt_sd = sd(rt)), by = .(period, school_year, mcq, course)]
rt_mean[, school_year := factor(school_year, levels = c("18/19", "19/20"))]
rt_mean
ggplot(rt_mean, aes(x = period, y = rt, colour = school_year, lty = mcq, group = interaction(mcq, school_year))) +
  facet_grid(~ course) +
  geom_line() +
  geom_point() +
  scale_y_continuous(limits = c(1500, 3000)) +
  theme_paper

Combination plot

p_legend <- get_legend(p_acc)
Warning: Removed 24 row(s) containing missing values (geom_path).
p_acc <- p_acc +
  guides(colour = FALSE, fill = FALSE, lty = FALSE)

p_rt <- p_rt +
  guides(colour = FALSE, fill = FALSE, lty = FALSE)

Combine plots:

plot_grid(
  plot_grid(p_acc, p_rt,
            ncol = 1,
            labels = c("A", "B")),
  p_legend,
  rel_widths = c(1, .2)
)
Warning: Removed 24 row(s) containing missing values (geom_path).

Warning: Removed 24 row(s) containing missing values (geom_path).

ggsave("../output/combi_acc_rt.pdf", width = 9, height = 3.5)
ggsave("../output/combi_acc_rt.eps", width = 9, height = 3.5)
Warning in grid.Call.graphics(C_rect, x$x, x$y, x$width, x$height,
resolveHJust(x$just, : semi-transparency is not supported on this device:
reported only once per page
ggsave("../output/combi_acc_rt.png", width = 9, height = 3.5)

Learning progress

Get the unique book chapter IDs on each day:

db <- db_connect()

progress <- dbGetQuery(db,
                       "SELECT DISTINCT r.book_info_id AS 'book_info_id',
                        r.method AS 'method',
                        DATE(r.date + 3600, 'unixepoch') AS 'doy',
                        COUNT(*) AS 'trials'
                        FROM 'responses_noduplicates' r
                        GROUP BY r.method,
                        r.book_info_id,
                        DATE(r.date + 3600, 'unixepoch');"
                       )

db_disconnect(db)

setDT(progress)

Join with the book chapter information:

db <- db_connect()
book_info <- dbGetQuery(db, "SELECT DISTINCT * FROM 'book_info'")
db_disconnect(db)

setDT(book_info)
progress[book_info[book_type == "Hoofdboek",], on  = "book_info_id", c("method_group", "book_title", "chapter") := .(i.method_group, i.book_title, i.chapter)]

Add sensible course names:

progress[, course := ifelse(method == "Grandes Lignes", "French", ifelse(method == "Stepping Stones", "English", "German"))]

Add a school year column (cutoff date: 1 August):

progress[, doy_posix := as.POSIXct(doy)]
progress[, school_year := ifelse(doy_posix < "2019-08-01", "18/19", "19/20")]

Consolidate count by day and chapter:

progress_by_day <- progress[, .(trials = sum(trials)), by = .(school_year, doy_posix, course, method_group, book_title, chapter)]

Simplify level names:

# Keep all distinctions
progress_by_day[, book_title_simple := stringr::str_sub(book_title, 3, -10)]
progress_by_day[, book_title_simple := factor(book_title_simple, levels = c("vmbo b/lwoo", "vmbo b", "vmbo bk", "vmbo k", "vmbo kgt", "vmbo-gt", "vmbo gt", "vmbo-gt/havo", "vmbo (t)hv", "havo", "havo vwo", "vwo"))]
# Simplify to three levels
progress_by_day[, level := dplyr::case_when(
  grepl( "hv", book_title) ~ "General secondary (havo)",
  grepl("vmbo", book_title) ~ "Pre-vocational (vmbo)",
  grepl("havo", book_title) ~ "General secondary (havo)",
  grepl("vwo", book_title) ~ "Pre-university (vwo)",
  TRUE ~ "Other")]
progress_by_day[, level := factor(level, levels = c("Other", "Pre-vocational (vmbo)", "General secondary (havo)", "Pre-university (vwo)"))]

Simplify year names:

progress_by_day[, year := dplyr::case_when(
  method_group == "Leerjaar 1 (5e Ed.)" ~ "Year 1",
  method_group == "Leerjaar 2 (5e Ed.)" ~ "Year 2",
  method_group == "Leerjaar 3 (5e Ed.)" ~ "Year 3",
  method_group == "Leerjaar 3/4 (5e Ed.)" ~ "Year 3/4",
  method_group == "Leerjaar 4 (5e Ed.)" ~ "Year 4",
  method_group == "Tweede Fase (6e Ed.)" ~ "Tweede Fase",
  TRUE ~ "Other")]

Simplify chapter names:

# In most cases, the chapter name starts with a number
progress_by_day[, chapter_simple := factor(as.numeric(stringr::str_extract(chapter, "^\\d{1,2}")))]
# Remaining cases:
unique(progress_by_day[is.na(chapter_simple),]$chapter)
 [1] "BS2 Dienstleistung"               
 [2] "BS5 Reisen"                       
 [3] "BS1 Familie und Beziehungen"      
 [4] "BS3 Dienstleistung"               
 [5] "BS4 Reisen und Verkehr"           
 [6] "Lernliste Brückenschlag"          
 [7] "BS2 Freizeit"                     
 [8] "BS1 Schule und Ausbildung"        
 [9] "Bridging the Gap Year 2"          
[10] "Bridging the Gap Year 1"          
[11] "Bridging the Gap Exam Preparation"
[12] "Exam Preparation"                 
[13] "Bridging the Gap mbo"             
[14] "Bridging the Gap havo"            
BS2 Dienstleistung

BS5 Reisen

BS1 Familie und Beziehungen

BS3 Dienstleistung

BS4 Reisen und Verkehr

Lernliste Brückenschlag

BS2 Freizeit

BS1 Schule und Ausbildung

Bridging the Gap Year 2

Bridging the Gap Year 1

Bridging the Gap Exam Preparation

Exam Preparation

Bridging the Gap mbo

Bridging the Gap havo
# Combine these chapters into an "other" category
progress_by_day[is.na(chapter_simple), chapter_simple := "O"]

Align school years:

progress_by_day[school_year == "18/19", doy_posix_aligned := as.POSIXct(doy_posix + 365*24*60*60, origin = "1970-01-01")]
progress_by_day[school_year == "19/20", doy_posix_aligned := doy_posix]

Use cut.Date() to bin dates by week and month. Each day is assigned the date of the most recent Monday.

progress_by_day[, doy_posix_aligned_week := cut.POSIXt(doy_posix_aligned, "week")]
progress_by_day[, doy_posix_aligned_month := cut.POSIXt(doy_posix_aligned, "month")]

Calculate proportions by week and month:

progress_by_week <- progress_by_day[, .(trials = sum(trials)), by = .(school_year, doy_posix_aligned_week, course, level, year, chapter_simple)]
progress_by_week[, prop := trials/sum(trials), by = .(school_year, doy_posix_aligned_week, course, level, year)]
progress_by_month <- progress_by_day[, .(trials = sum(trials)), by = .(school_year, doy_posix_aligned_month, course, level, year, chapter_simple)]
progress_by_month[, prop := trials/sum(trials), by = .(school_year, doy_posix_aligned_month, course, level, year)]
setorder(progress_by_week, chapter_simple)
setorder(progress_by_month, chapter_simple)

French

p_french_y1 <- ggplot(progress_by_week[course == "French" & year == "Year 1"], aes(x = as.POSIXct(doy_posix_aligned_week), y = prop, fill = chapter_simple, group = school_year)) +
  facet_grid(school_year ~ level) +
  geom_col(alpha = 0.75, width = 7*24*60*60, colour = NA) +
  geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -0.01, ymax = 1.01, fill = NA, colour = "black", lty = 2) +
  scale_x_datetime(expand = c(0, 0), 
                   breaks = as.POSIXct(c(
                     "2019-10-01 02:00:00 CET",
                     "2019-12-01 02:00:00 CET",
                     "2020-02-01 02:00:00 CET",
                     "2020-04-01 02:00:00 CET",
                     "2020-06-01 02:00:00 CET")),
                   limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
                   date_labels = "%b") +
  scale_y_continuous(labels = scales::percent_format(), limits = c(0,1), breaks = c(0, .5 , 1)) +
  scale_fill_viridis_d(direction = -1, na.translate = FALSE) +
  guides(fill = guide_legend(ncol = 2)) +
  labs(x = NULL,
       y = "Share of trials",
       fill = "Chapter") +
  theme_paper

p_french_y2 <- ggplot(progress_by_week[course == "French" & year == "Year 2"], aes(x = as.POSIXct(doy_posix_aligned_week), y = prop, fill = chapter_simple, group = school_year)) +
  facet_grid(school_year ~ level) +
  geom_col(alpha = 0.75, width = 7*24*60*60, colour = NA) +
  geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -0.01, ymax = 1.01, fill = NA, colour = "black", lty = 2) +
  scale_x_datetime(expand = c(0, 0), 
                   breaks = as.POSIXct(c(
                     "2019-10-01 02:00:00 CET",
                     "2019-12-01 02:00:00 CET",
                     "2020-02-01 02:00:00 CET",
                     "2020-04-01 02:00:00 CET",
                     "2020-06-01 02:00:00 CET")),
                   limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
                   date_labels = "%b") +
  scale_y_continuous(labels = scales::percent_format(), limits = c(0,1), breaks = c(0, .5 , 1)) +
  scale_fill_viridis_d(direction = -1, na.translate = FALSE) +
  guides(fill = guide_legend(ncol = 2)) +
  labs(x = NULL,
       y = "Share of trials",
       fill = "Chapter") +
  theme_paper

p_french_y3 <- ggplot(progress_by_week[course == "French" & year == "Year 3/4"], aes(x = as.POSIXct(doy_posix_aligned_week), y = prop, fill = chapter_simple, group = school_year)) +
  facet_grid(school_year ~ level) +
  geom_col(alpha = 0.75, width = 7*24*60*60, colour = NA) +
  geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -0.01, ymax = 1.01, fill = NA, colour = "black", lty = 2) +
  scale_x_datetime(expand = c(0, 0), 
                   breaks = as.POSIXct(c(
                     "2019-10-01 02:00:00 CET",
                     "2019-12-01 02:00:00 CET",
                     "2020-02-01 02:00:00 CET",
                     "2020-04-01 02:00:00 CET",
                     "2020-06-01 02:00:00 CET")),
                   limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
                   date_labels = "%b") +
  scale_y_continuous(labels = scales::percent_format(), limits = c(0,1), breaks = c(0, .5 , 1)) +
  scale_fill_viridis_d(direction = -1, na.translate = FALSE) +
  guides(fill = guide_legend(ncol = 2)) +
  labs(x = NULL,
       y = "Share of trials",
       fill = "Chapter") +
  theme_paper

p_progress_french <- plot_grid(p_french_y1, p_french_y2, p_french_y3, 
          ncol = 1,
          align = "hv", axis = "tblr",
          labels = c("Year 1", "Year 2", "Year 3/4"),
          hjust = -0.1,
          scale = .95)
Warning: Removed 48 rows containing missing values (position_stack).
Warning: Removed 40 rows containing missing values (geom_col).
Warning: Removed 46 rows containing missing values (position_stack).
Warning: Removed 41 rows containing missing values (geom_col).
Warning: Removed 24 rows containing missing values (position_stack).
Warning: Removed 23 rows containing missing values (geom_col).
p_progress_french

ggsave("../output/progress_french.pdf", width = 9, height = 9)
ggsave("../output/progress_french.eps", width = 9, height = 9)
Warning in grid.Call.graphics(C_rect, x$x, x$y, x$width, x$height,
resolveHJust(x$just, : semi-transparency is not supported on this device:
reported only once per page
ggsave("../output/progress_french.png", width = 9, height = 6)

Did the share of trials change between school years? We can simplify the analysis by aggregating over the whole lockdown period.

progress_lockdown <- progress_by_day[between(doy_posix_aligned, date_schools_closed, date_schools_opened), .(trials = sum(trials)), by = .(school_year, course, level, year, chapter_simple)]

# Fill in missing rows (occurs when chapter was only studied in one of the two years)
progress_lockdown <- as.data.table(tidyr::complete(progress_lockdown, tidyr::nesting(course, level, year, chapter_simple), school_year, fill = list(trials = 0))) 
  
progress_lockdown[, prop := trials/sum(trials), by = .(school_year, course, level, year)]
setorder(progress_lockdown, chapter_simple)
ggplot(progress_lockdown[course == "French"], aes(x = school_year, y = prop, fill = chapter_simple, group = school_year)) +
  facet_grid(level ~ year) +
  geom_col(colour = NA) +
  scale_y_continuous(labels = scales::percent_format(), limits = c(0,1)) +
  scale_fill_viridis_d(direction = -1, na.translate = FALSE) +
  labs(x = NULL,
       y = "Share of trials",
       fill = "Chapter",
       title = "French") +
  theme_paper

Perform a chi-square test of homogeneity to determine whether school years are significantly different.

for (y in sort(unique(progress_lockdown$year))) {
  for (l in levels(progress_lockdown$level)) {
    d <- progress_lockdown[course == "French" & year == y & level == l]
    if (nrow(d) > 0) {
      print(paste("French", y, l, collapse= " "))
      print(
        chisq.test(
          dcast(d, school_year ~ chapter_simple, value.var = "trials", fill = 0)[, school_year := NULL]
        )
      )
    }
  } 
}
[1] "French Year 1 Pre-vocational (vmbo)"

    Pearson's Chi-squared test

data:  dcast(d, school_year ~ chapter_simple, value.var = "trials",     fill = 0)[, `:=`(school_year, NULL)]
X-squared = 26799, df = 5, p-value < 2.2e-16

[1] "French Year 1 General secondary (havo)"

    Pearson's Chi-squared test

data:  dcast(d, school_year ~ chapter_simple, value.var = "trials",     fill = 0)[, `:=`(school_year, NULL)]
X-squared = 229477, df = 5, p-value < 2.2e-16

[1] "French Year 1 Pre-university (vwo)"

    Pearson's Chi-squared test

data:  dcast(d, school_year ~ chapter_simple, value.var = "trials",     fill = 0)[, `:=`(school_year, NULL)]
X-squared = 40312, df = 5, p-value < 2.2e-16

[1] "French Year 2 Pre-vocational (vmbo)"

    Pearson's Chi-squared test

data:  dcast(d, school_year ~ chapter_simple, value.var = "trials",     fill = 0)[, `:=`(school_year, NULL)]
X-squared = 13882, df = 5, p-value < 2.2e-16

[1] "French Year 2 General secondary (havo)"

    Pearson's Chi-squared test

data:  dcast(d, school_year ~ chapter_simple, value.var = "trials",     fill = 0)[, `:=`(school_year, NULL)]
X-squared = 50486, df = 5, p-value < 2.2e-16

[1] "French Year 2 Pre-university (vwo)"

    Pearson's Chi-squared test

data:  dcast(d, school_year ~ chapter_simple, value.var = "trials",     fill = 0)[, `:=`(school_year, NULL)]
X-squared = 46882, df = 5, p-value < 2.2e-16

[1] "French Year 3/4 Pre-vocational (vmbo)"

    Pearson's Chi-squared test

data:  dcast(d, school_year ~ chapter_simple, value.var = "trials",     fill = 0)[, `:=`(school_year, NULL)]
X-squared = 30269, df = 7, p-value < 2.2e-16

[1] "French Year 3/4 General secondary (havo)"

    Pearson's Chi-squared test

data:  dcast(d, school_year ~ chapter_simple, value.var = "trials",     fill = 0)[, `:=`(school_year, NULL)]
X-squared = 23169, df = 5, p-value < 2.2e-16

[1] "French Year 3/4 Pre-university (vwo)"

    Pearson's Chi-squared test

data:  dcast(d, school_year ~ chapter_simple, value.var = "trials",     fill = 0)[, `:=`(school_year, NULL)]
X-squared = 21545, df = 5, p-value < 2.2e-16

Conclusion: all tests indicate a difference in proportions between school years (p << 0.001).

Visualise the change between school years:

progress_lockdown[, prop_change := prop[school_year == "19/20"] - prop[school_year == "18/19"], by = .(course, level, year, chapter_simple)]
ggplot(progress_lockdown[school_year == "19/20" & course == "French"], aes(x = chapter_simple, y = (prop_change * 100), colour = chapter_simple, group = school_year)) +
  facet_grid(level ~ year, scales = "free_x") +
  geom_hline(yintercept = 0, lty = 2) +
  geom_segment(aes(xend = chapter_simple), yend = 0) +
  geom_point() +
  scale_y_continuous(limits = c(-25, 25)) +
  scale_colour_viridis_d(direction = -1, na.translate = FALSE) +
  guides(colour = FALSE) +
  labs(x = "Chapter",
       y = "Change in trial share\n(percentage points)",
       title = "French") +
  theme_paper

Are these changes really important? We may expect a certain amount of fluctuation between any pair of school years. We don’t have data from before the 18/19 school year, but we can look at how the magnitude of changes during the lockdown period compares to changes earlier in the school year.

To keep things as comparable as possible, use a sliding time window with the same size as the lockdown period:

time_window <- as.numeric(round(date_schools_opened - date_schools_closed))
time_window
[1] 78
date_range <- sort(unique(progress_by_day$doy_posix_aligned))
date_range <- date_range[date_range < date_schools_closed]

prop_change_window <- data.table()

for (i in 1:(length(date_range) - as.numeric(time_window))) {
  d <- date_range[i:(i + time_window - 1)]
  progress_window <- progress_by_day[course %in% c("French", "English") & doy_posix_aligned %in% d,
                                     .(trials = sum(trials)),
                                     by = .(school_year, course, level, year, chapter_simple)]
  
  # Fill in missing rows (occurs when chapter was only studied in one of the two years)
  progress_window <- as.data.table(tidyr::complete(progress_window, tidyr::nesting(course, level, year, chapter_simple), school_year, fill = list(trials = 0))) 
  
  progress_window[, prop := trials/sum(trials), by = .(school_year, course, level, year)]

  progress_window[, prop_change := prop[school_year == "19/20"] - prop[school_year == "18/19"], by = .(course, level, year, chapter_simple)]
  
  prop_change_window <- rbind(prop_change_window, progress_window[school_year == "19/20",][,window := i][,date_min := min(d)][,date_max := max(d)])
}

The density of year-to-year changes can be visualised by time window:

ggplot(prop_change_window, aes(x = prop_change * 100, y = window, group = window)) +
  ggridges::geom_density_ridges(alpha = 0.1, scale = 25, fill = NA) +
  labs(x = "Change in trial share\n(percentage points)",
         y = "Time window") +
  theme_paper
Picking joint bandwidth of 0.584

Compare the aggregated density to the changes during the lockdown period:

prop_change_combined <- rbind(prop_change_window[, period := "Pre-lockdown"], progress_lockdown[course %in% c("French", "English") & school_year == "19/20", period := "Lockdown"], fill = TRUE)
prop_change_combined[, period := factor(period, levels = c("Pre-lockdown", "Lockdown"))]
ggplot(prop_change_combined, aes(x = prop_change, colour = period)) +
  geom_density() +
  scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
  labs(x = "Change in trial share\n(percentage points)",
       y = "Density",
       colour = NULL) +
  theme_paper

prop_change_sd <- prop_change_window[, .(sd = sd(prop_change) * 100), by = .(course, year, level)]

Add boundaries based on the typical spread to the change plot:

p_change_french <- ggplot(progress_lockdown[school_year == "19/20" & course == "French"], aes(colour = chapter_simple)) +
  facet_grid(year ~ level, scales = "free_x") +
  geom_rect(data = prop_change_sd[course == "French"], aes(ymin = -2*sd, ymax = 2*sd), xmin = 0, xmax = 1000, fill = "grey90", colour = NA) +
  geom_rect(data = prop_change_sd[course == "French"], aes(ymin = -sd, ymax = sd), xmin = 0, xmax = 100, fill = "grey75", colour = NA) +
  geom_hline(yintercept = 0, lty = 2) +
  geom_segment(aes(x = chapter_simple, xend = chapter_simple, y = (prop_change * 100)), yend = 0) +
  geom_point(aes(x = chapter_simple, y = (prop_change * 100))) +
  scale_y_continuous(breaks = c(-20, 0, 20)) +
  scale_colour_viridis_d(direction = -1, na.translate = FALSE) +
  guides(colour = FALSE) +
  labs(x = "Chapter",
       y = "Change in trial share\n(percentage points)") +
  theme_paper +
  theme(panel.grid.major.y = element_blank())

p_change_french

ggsave("../output/progress_change_french.pdf", width = 5, height = 4)
ggsave("../output/progress_change_french.eps", width = 5, height = 4)
ggsave("../output/progress_change_french.png", width = 9, height = 3)

Make a combination plot for in the paper:

plot_grid(p_french_y1, p_french_y2, p_french_y3, p_change_french,
          ncol = 1,
          align = "hv", axis = "tblr",
          labels = c("Year 1", "Year 2", "Year 3/4", "Change"),
          rel_heights = c(1, 1, 1, 1.5),
          hjust = -0.1,
          scale = .95)
Warning: Removed 48 rows containing missing values (position_stack).
Warning: Removed 40 rows containing missing values (geom_col).
Warning: Removed 46 rows containing missing values (position_stack).
Warning: Removed 41 rows containing missing values (geom_col).
Warning: Removed 24 rows containing missing values (position_stack).
Warning: Removed 23 rows containing missing values (geom_col).

ggsave("../output/progress_combi_french.pdf", width = 9, height = 9)
ggsave("../output/progress_combi_french.eps", width = 9, height = 9)
Warning in grid.Call.graphics(C_rect, x$x, x$y, x$width, x$height,
resolveHJust(x$just, : semi-transparency is not supported on this device:
reported only once per page
ggsave("../output/progress_combi_french.png", width = 9, height = 9)
p_change_french_y1 <- ggplot(progress_lockdown[school_year == "19/20" & course == "French" & year == "Year 1"], aes(colour = chapter_simple)) +
  facet_grid(. ~ level, scales = "free_x") +
  geom_rect(data = prop_change_sd[course == "French" & year == "Year 1"], aes(ymin = -2*sd, ymax = 2*sd), xmin = 0, xmax = 1000, fill = "grey90", colour = NA) +
  geom_rect(data = prop_change_sd[course == "French" & year == "Year 1"], aes(ymin = -sd, ymax = sd), xmin = 0, xmax = 100, fill = "grey75", colour = NA) +
  geom_hline(yintercept = 0, lty = 2) +
  geom_segment(aes(x = chapter_simple, xend = chapter_simple, y = (prop_change * 100)), yend = 0, alpha = .75) +
  geom_point(aes(x = chapter_simple, y = (prop_change * 100)), alpha = .75) +
  scale_y_continuous(breaks = c(-20, 0, 20), limits = c(-30, 30), labels = scales::number_format(suffix = " pp")) +
  scale_colour_viridis_d(direction = -1, na.translate = FALSE) +
  guides(colour = FALSE) +
  labs(x = NULL,
       y = "Change") +
  theme_paper +
  theme(panel.grid.major.y = element_blank(),
        strip.background = element_blank(),
        strip.text.x = element_blank(),
        panel.background = element_rect(fill = "transparent", colour = NA),
        plot.background = element_rect(fill = "transparent", colour = NA))

p_change_french_y2 <- ggplot(progress_lockdown[school_year == "19/20" & course == "French" & year == "Year 2"], aes(colour = chapter_simple)) +
  facet_grid(. ~ level, scales = "free_x") +
  geom_rect(data = prop_change_sd[course == "French" & year == "Year 2"], aes(ymin = -2*sd, ymax = 2*sd), xmin = 0, xmax = 1000, fill = "grey90", colour = NA) +
  geom_rect(data = prop_change_sd[course == "French" & year == "Year 2"], aes(ymin = -sd, ymax = sd), xmin = 0, xmax = 100, fill = "grey75", colour = NA) +
  geom_hline(yintercept = 0, lty = 2) +
  geom_segment(aes(x = chapter_simple, xend = chapter_simple, y = (prop_change * 100)), yend = 0, alpha = .75) +
  geom_point(aes(x = chapter_simple, y = (prop_change * 100)), alpha = .75) +
  scale_y_continuous(breaks = c(-20, 0, 20),  limits = c(-30, 30), labels = scales::number_format(suffix = " pp")) +
  scale_colour_viridis_d(direction = -1, na.translate = FALSE) +
  guides(colour = FALSE) +
  labs(x = NULL,
       y = "Change") +
  theme_paper +
  theme(panel.grid.major.y = element_blank(),
        strip.background = element_blank(),
        strip.text.x = element_blank(),
        panel.background = element_rect(fill = "transparent", colour = NA),
        plot.background = element_rect(fill = "transparent", colour = NA))

p_change_french_y3 <- ggplot(progress_lockdown[school_year == "19/20" & course == "French" & year == "Year 3/4"], aes(colour = chapter_simple)) +
  facet_grid(. ~ level, scales = "free_x") +
  geom_rect(data = prop_change_sd[course == "French" & year == "Year 3/4"], aes(ymin = -2*sd, ymax = 2*sd), xmin = 0, xmax = 1000, fill = "grey90", colour = NA) +
  geom_rect(data = prop_change_sd[course == "French" & year == "Year 3/4"], aes(ymin = -sd, ymax = sd), xmin = 0, xmax = 100, fill = "grey75", colour = NA) +
  geom_hline(yintercept = 0, lty = 2) +
  geom_segment(aes(x = chapter_simple, xend = chapter_simple, y = (prop_change * 100)), yend = 0, alpha = .75) +
  geom_point(aes(x = chapter_simple, y = (prop_change * 100)), alpha = .75) +
  scale_y_continuous(breaks = c(-20, 0, 20),  limits = c(-30, 30), labels = scales::number_format(suffix = " pp")) +
  scale_colour_viridis_d(direction = -1, na.translate = FALSE) +
  guides(colour = FALSE) +
  labs(x = NULL,
       y = "Change") +
  theme_paper +
  theme(panel.grid.major.y = element_blank(),
        strip.background = element_blank(),
        strip.text.x = element_blank(),
        panel.background = element_rect(fill = "transparent", colour = NA),
        plot.background = element_rect(fill = "transparent", colour = NA))

filler_plot <- qplot() + 
  theme_nothing() + 
  theme(panel.background = element_rect(fill = "transparent", colour = NA),
        plot.background = element_rect(fill = "transparent", colour = NA))

plot_grid(
          filler_plot,
          p_french_y1, filler_plot, p_change_french_y1, filler_plot,
          p_french_y2, filler_plot, p_change_french_y2, filler_plot, 
          p_french_y3, filler_plot, p_change_french_y3,
          ncol = 1,
          align = "hv", axis = "tblr",
          labels = c(NA,
                     "Year 1", NA, NA, NA,
                     "Year 2", NA, NA, NA,
                     "Year 3/4", NA, NA),
          rel_heights = c(.1, 
                          1, -.2, .75, .1,
                          1, -.2, .75, .1,
                          1, -.2, .75),
          hjust = -0.1,
          vjust = -0.1,
          scale = .95)
Warning: Removed 48 rows containing missing values (position_stack).
Warning: Removed 40 rows containing missing values (geom_col).
Warning: Removed 46 rows containing missing values (position_stack).
Warning: Removed 41 rows containing missing values (geom_col).
Warning: Removed 24 rows containing missing values (position_stack).
Warning: Removed 23 rows containing missing values (geom_col).
Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

ggsave("../output/progress_combi_alt_french.pdf", width = 9, height = 9)
Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).
ggsave("../output/progress_combi_alt_french.eps", width = 9, height = 9)
Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).
Warning in grid.Call.graphics(C_rect, x$x, x$y, x$width, x$height,
resolveHJust(x$just, : semi-transparency is not supported on this device:
reported only once per page
ggsave("../output/progress_combi_alt_french.png", width = 9, height = 9)
Warning: Removed 1 rows containing missing values (geom_text).
Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

English

NOTE: chapters without a number (Bridging the Gap, Exam Preparation) are shown as “O” in the plot. They don’t seem to fit neatly in the chapter sequence, so I’m grouping them together.

p_english_y1 <- ggplot(progress_by_week[level != "Other"][, level := factor(level)][course == "English" & year == "Year 1"], aes(x = as.POSIXct(doy_posix_aligned_week), y = prop, fill = chapter_simple, group = school_year)) +
  facet_grid(school_year ~ level, drop = FALSE) +
  geom_col(alpha = 0.75, width = 7*24*60*60, colour = NA) +
  geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -0.01, ymax = 1.01, fill = NA, colour = "black", lty = 2) +
  scale_x_datetime(expand = c(0, 0), 
                   breaks = as.POSIXct(c(
                     "2019-10-01 02:00:00 CET",
                     "2019-12-01 02:00:00 CET",
                     "2020-02-01 02:00:00 CET",
                     "2020-04-01 02:00:00 CET",
                     "2020-06-01 02:00:00 CET")),
                   limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
                   date_labels = "%b") +
  scale_y_continuous(labels = scales::percent_format(), limits = c(0,1), breaks = c(0, .5 , 1)) +
  scale_fill_viridis_d(direction = -1, na.translate = FALSE) +
  guides(fill = guide_legend(ncol = 2)) +
  labs(x = NULL,
       y = "Share of trials",
       fill = "Chapter") +
  theme_paper

p_english_y2 <- ggplot(progress_by_week[level != "Other"][, level := factor(level)][course == "English" & year == "Year 2"], aes(x = as.POSIXct(doy_posix_aligned_week), y = prop, fill = chapter_simple, group = school_year)) +
  facet_grid(school_year ~ level, drop = FALSE) +
  geom_col(alpha = 0.75, width = 7*24*60*60, colour = NA) +
  geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -0.01, ymax = 1.01, fill = NA, colour = "black", lty = 2) +
  scale_x_datetime(expand = c(0, 0), 
                   breaks = as.POSIXct(c(
                     "2019-10-01 02:00:00 CET",
                     "2019-12-01 02:00:00 CET",
                     "2020-02-01 02:00:00 CET",
                     "2020-04-01 02:00:00 CET",
                     "2020-06-01 02:00:00 CET")),
                   limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
                   date_labels = "%b") +
  scale_y_continuous(labels = scales::percent_format(), limits = c(0,1), breaks = c(0, .5 , 1)) +
  scale_fill_viridis_d(direction = -1, na.translate = FALSE) +
  guides(fill = guide_legend(ncol = 2)) +
  labs(x = NULL,
       y = "Share of trials",
       fill = "Chapter") +
  theme_paper

p_english_y3 <- ggplot(progress_by_week[level != "Other"][, level := factor(level)][course == "English" & year == "Year 3"], aes(x = as.POSIXct(doy_posix_aligned_week), y = prop, fill = chapter_simple, group = school_year)) +
  facet_grid(school_year ~ level, drop = FALSE) +
  geom_col(alpha = 0.75, width = 7*24*60*60, colour = NA) +
  geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -0.01, ymax = 1.01, fill = NA, colour = "black", lty = 2) +
  scale_x_datetime(expand = c(0, 0), 
                   breaks = as.POSIXct(c(
                     "2019-10-01 02:00:00 CET",
                     "2019-12-01 02:00:00 CET",
                     "2020-02-01 02:00:00 CET",
                     "2020-04-01 02:00:00 CET",
                     "2020-06-01 02:00:00 CET")),
                   limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
                   date_labels = "%b") +
  scale_y_continuous(labels = scales::percent_format(), limits = c(0,1), breaks = c(0, .5 , 1)) +
  scale_fill_viridis_d(direction = -1, na.translate = FALSE) +
  guides(fill = guide_legend(ncol = 2)) +
  labs(x = NULL,
       y = "Share of trials",
       fill = "Chapter") +
  theme_paper

p_english_y4 <- ggplot(progress_by_week[level != "Other"][, level := factor(level)][course == "English" & year == "Year 4"], aes(x = as.POSIXct(doy_posix_aligned_week), y = prop, fill = chapter_simple, group = school_year)) +
  facet_grid(school_year ~ level, drop = FALSE) +
  geom_col(alpha = 0.75, width = 7*24*60*60, colour = NA) +
  geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -0.01, ymax = 1.01, fill = NA, colour = "black", lty = 2) +
  scale_x_datetime(expand = c(0, 0), 
                   breaks = as.POSIXct(c(
                     "2019-10-01 02:00:00 CET",
                     "2019-12-01 02:00:00 CET",
                     "2020-02-01 02:00:00 CET",
                     "2020-04-01 02:00:00 CET",
                     "2020-06-01 02:00:00 CET")),
                   limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
                   date_labels = "%b") +
  scale_y_continuous(labels = scales::percent_format(), limits = c(0,1), breaks = c(0, .5 , 1)) +
  scale_fill_viridis_d(direction = -1, na.translate = FALSE) +
  guides(fill = guide_legend(ncol = 2)) +
  labs(x = NULL,
       y = "Share of trials",
       fill = "Chapter") +
  theme_paper

p_progress_english <- plot_grid(p_english_y1, p_english_y2, p_english_y3, p_english_y4,
          ncol = 1,
          align = "hv", axis = "tblr",
          labels = c("Year 1", "Year 2", "Year 3", "Year 4"),
          hjust = -0.1,
          scale = .95)
Warning: Removed 61 rows containing missing values (position_stack).
Warning: Removed 61 rows containing missing values (geom_col).
Warning: Removed 69 rows containing missing values (position_stack).
Warning: Removed 60 rows containing missing values (geom_col).
Warning: Removed 49 rows containing missing values (position_stack).
Warning: Removed 50 rows containing missing values (geom_col).
Warning: Removed 7 rows containing missing values (position_stack).
Warning: Removed 4 rows containing missing values (geom_col).
p_progress_english

ggsave("../output/progress_english.pdf", width = 9, height = 9)
ggsave("../output/progress_english.eps", width = 9, height = 9)
Warning in grid.Call.graphics(C_rect, x$x, x$y, x$width, x$height,
resolveHJust(x$just, : semi-transparency is not supported on this device:
reported only once per page
ggsave("../output/progress_english.png", width = 9, height = 9)

Did the share of trials change between school years?

ggplot(progress_lockdown[course == "English" & level != "Other"], aes(x = school_year, y = prop, fill = chapter_simple, group = school_year)) +
  facet_grid(level ~ year) +
  geom_col(colour = NA) +
  scale_y_continuous(labels = scales::percent_format(), limits = c(0,1)) +
  scale_fill_viridis_d(direction = -1, na.translate = FALSE) +
  labs(x = NULL,
       y = "Share of trials",
       fill = "Chapter",
       title = "English") +
  theme_paper

Change between school years:

p_change_english <- ggplot(progress_lockdown[school_year == "19/20" & course == "English" & level != "Other"], aes(colour = chapter_simple)) +
  facet_grid(year ~ level, scales = "free_x") +
  geom_rect(data = prop_change_sd[course == "English" & level != "Other"], aes(ymin = -2*sd, ymax = 2*sd), xmin = 0, xmax = 1000, fill = "grey90", colour = NA) +
  geom_rect(data = prop_change_sd[course == "English" & level != "Other"], aes(ymin = -sd, ymax = sd), xmin = 0, xmax = 100, fill = "grey75", colour = NA) +
  geom_hline(yintercept = 0, lty = 2) +
  geom_segment(aes(x = chapter_simple, xend = chapter_simple, y = (prop_change * 100)), yend = 0) +
  geom_point(aes(x = chapter_simple, y = (prop_change * 100))) +
  scale_colour_viridis_d(direction = -1, na.translate = FALSE) +
  guides(colour = FALSE) +
  labs(x = "Chapter",
       y = "Change in trial share\n(percentage points)") +
  theme_paper +
  theme(panel.grid.major.y = element_blank())

p_change_english 

ggsave("../output/progress_change_english.pdf", width = 9, height = 6)
ggsave("../output/progress_change_english.eps", width = 9, height = 6)
ggsave("../output/progress_change_english.png", width = 9, height = 6)

Perform a chi-square test of homogeneity to determine whether school years are significantly different.

for (y in sort(unique(progress_lockdown$year))) {
  for (l in levels(progress_lockdown$level)) {
    d <- progress_lockdown[course == "English" & year == y & level == l]
    if (nrow(d) > 0) {
      print(paste("English", y, l, collapse= " "))
      print(
        chisq.test(
          dcast(d, school_year ~ chapter_simple, value.var = "trials", fill = 0)[, school_year := NULL]
        )
      )
    }
  } 
}
[1] "English Year 1 Other"

    Pearson's Chi-squared test

data:  dcast(d, school_year ~ chapter_simple, value.var = "trials",     fill = 0)[, `:=`(school_year, NULL)]
X-squared = 1902.8, df = 7, p-value < 2.2e-16

[1] "English Year 1 Pre-vocational (vmbo)"

    Pearson's Chi-squared test

data:  dcast(d, school_year ~ chapter_simple, value.var = "trials",     fill = 0)[, `:=`(school_year, NULL)]
X-squared = 59650, df = 7, p-value < 2.2e-16

[1] "English Year 1 General secondary (havo)"

    Pearson's Chi-squared test

data:  dcast(d, school_year ~ chapter_simple, value.var = "trials",     fill = 0)[, `:=`(school_year, NULL)]
X-squared = 236327, df = 7, p-value < 2.2e-16

[1] "English Year 1 Pre-university (vwo)"

    Pearson's Chi-squared test

data:  dcast(d, school_year ~ chapter_simple, value.var = "trials",     fill = 0)[, `:=`(school_year, NULL)]
X-squared = 47589, df = 7, p-value < 2.2e-16

[1] "English Year 2 Other"

    Pearson's Chi-squared test

data:  dcast(d, school_year ~ chapter_simple, value.var = "trials",     fill = 0)[, `:=`(school_year, NULL)]
X-squared = 4117.3, df = 7, p-value < 2.2e-16

[1] "English Year 2 Pre-vocational (vmbo)"

    Pearson's Chi-squared test

data:  dcast(d, school_year ~ chapter_simple, value.var = "trials",     fill = 0)[, `:=`(school_year, NULL)]
X-squared = 26122, df = 8, p-value < 2.2e-16

[1] "English Year 2 General secondary (havo)"

    Pearson's Chi-squared test

data:  dcast(d, school_year ~ chapter_simple, value.var = "trials",     fill = 0)[, `:=`(school_year, NULL)]
X-squared = 16303, df = 8, p-value < 2.2e-16

[1] "English Year 2 Pre-university (vwo)"

    Pearson's Chi-squared test

data:  dcast(d, school_year ~ chapter_simple, value.var = "trials",     fill = 0)[, `:=`(school_year, NULL)]
X-squared = 84027, df = 7, p-value < 2.2e-16

[1] "English Year 3 Other"

    Pearson's Chi-squared test

data:  dcast(d, school_year ~ chapter_simple, value.var = "trials",     fill = 0)[, `:=`(school_year, NULL)]
X-squared = 16118, df = 6, p-value < 2.2e-16

[1] "English Year 3 Pre-vocational (vmbo)"

    Pearson's Chi-squared test

data:  dcast(d, school_year ~ chapter_simple, value.var = "trials",     fill = 0)[, `:=`(school_year, NULL)]
X-squared = 20169, df = 6, p-value < 2.2e-16

[1] "English Year 3 General secondary (havo)"

    Pearson's Chi-squared test

data:  dcast(d, school_year ~ chapter_simple, value.var = "trials",     fill = 0)[, `:=`(school_year, NULL)]
X-squared = 60993, df = 8, p-value < 2.2e-16

[1] "English Year 3 Pre-university (vwo)"

    Pearson's Chi-squared test

data:  dcast(d, school_year ~ chapter_simple, value.var = "trials",     fill = 0)[, `:=`(school_year, NULL)]
X-squared = 41120, df = 7, p-value < 2.2e-16

[1] "English Year 4 Pre-vocational (vmbo)"

    Pearson's Chi-squared test

data:  dcast(d, school_year ~ chapter_simple, value.var = "trials",     fill = 0)[, `:=`(school_year, NULL)]
X-squared = 20187, df = 5, p-value < 2.2e-16

Conclusion: all tests indicate a difference in proportions between school years (p << 0.001).

Make a combination plot for in the paper:

progress_lockdown_english <- progress_lockdown[level != "Other" & school_year == "19/20" & course == "English"]
progress_lockdown_english[, level := factor(level)]
p_change_english_y1 <- ggplot(progress_lockdown_english[year == "Year 1"], aes(colour = chapter_simple)) +
  facet_grid(. ~ level, scales = "free_x", drop = FALSE) +
  geom_rect(data = prop_change_sd[course == "English" & level != "Other" & year == "Year 1"][,level := factor(level)], aes(ymin = -2*sd, ymax = 2*sd), xmin = 0, xmax = 1000, fill = "grey90", colour = NA) +
  geom_rect(data = prop_change_sd[course == "English" & level != "Other" & year == "Year 1"][,level := factor(level)], aes(ymin = -sd, ymax = sd), xmin = 0, xmax = 100, fill = "grey75", colour = NA) +
  geom_hline(yintercept = 0, lty = 2) +
  geom_segment(aes(x = chapter_simple, xend = chapter_simple, y = (prop_change * 100)), yend = 0, alpha = .75) +
  geom_point(aes(x = chapter_simple, y = (prop_change * 100)), alpha = .75) +
  scale_y_continuous(breaks = c(-10, 0, 10), limits = c(-20, 20), labels = scales::number_format(suffix = " pp")) +
  scale_colour_viridis_d(direction = -1, na.translate = FALSE) +
  guides(colour = FALSE) +
  labs(x = NULL,
       y = "Change") +
  theme_paper +
  theme(panel.grid.major.y = element_blank(),
        strip.background = element_blank(),
        strip.text.x = element_blank(),
        panel.background = element_rect(fill = "transparent", colour = NA),
        plot.background = element_rect(fill = "transparent", colour = NA))

p_change_english_y2 <- ggplot(progress_lockdown_english[year == "Year 2"], aes(colour = chapter_simple)) +
  facet_grid(. ~ level, scales = "free_x", drop = FALSE) +
  geom_rect(data = prop_change_sd[course == "English" & level != "Other" & year == "Year 2"][,level := factor(level)], aes(ymin = -2*sd, ymax = 2*sd), xmin = 0, xmax = 1000, fill = "grey90", colour = NA) +
  geom_rect(data = prop_change_sd[course == "English" & level != "Other" & year == "Year 2"][,level := factor(level)], aes(ymin = -sd, ymax = sd), xmin = 0, xmax = 100, fill = "grey75", colour = NA) +
  geom_hline(yintercept = 0, lty = 2) +
  geom_segment(aes(x = chapter_simple, xend = chapter_simple, y = (prop_change * 100)), yend = 0, alpha = .75) +
  geom_point(aes(x = chapter_simple, y = (prop_change * 100)), alpha = .75) +
  scale_y_continuous(breaks = c(-10, 0, 10), limits = c(-20, 20), labels = scales::number_format(suffix = " pp")) +
  scale_colour_viridis_d(direction = -1, na.translate = FALSE) +
  guides(colour = FALSE) +
  labs(x = NULL,
       y = "Change") +
  theme_paper +
  theme(panel.grid.major.y = element_blank(),
        strip.background = element_blank(),
        strip.text.x = element_blank(),
        panel.background = element_rect(fill = "transparent", colour = NA),
        plot.background = element_rect(fill = "transparent", colour = NA))

p_change_english_y3 <- ggplot(progress_lockdown_english[year == "Year 3"], aes(colour = chapter_simple)) +
  facet_grid(. ~ level, scales = "free_x", drop = FALSE) +
  geom_rect(data = prop_change_sd[course == "English" & level != "Other" & year == "Year 3"][,level := factor(level)], aes(ymin = -2*sd, ymax = 2*sd), xmin = 0, xmax = 1000, fill = "grey90", colour = NA) +
  geom_rect(data = prop_change_sd[course == "English" & level != "Other" & year == "Year 3"][,level := factor(level)], aes(ymin = -sd, ymax = sd), xmin = 0, xmax = 100, fill = "grey75", colour = NA) +
  geom_hline(yintercept = 0, lty = 2) +
  geom_segment(aes(x = chapter_simple, xend = chapter_simple, y = (prop_change * 100)), yend = 0, alpha = .75) +
  geom_point(aes(x = chapter_simple, y = (prop_change * 100)), alpha = .75) +
  scale_y_continuous(breaks = c(-10, 0, 10), limits = c(-20, 20), labels = scales::number_format(suffix = " pp")) +
  scale_colour_viridis_d(direction = -1, na.translate = FALSE) +
  guides(colour = FALSE) +
  labs(x = NULL,
       y = "Change") +
  theme_paper +
  theme(panel.grid.major.y = element_blank(),
        strip.background = element_blank(),
        strip.text.x = element_blank(),
        panel.background = element_rect(fill = "transparent", colour = NA),
        plot.background = element_rect(fill = "transparent", colour = NA))

p_change_english_y4 <- ggplot(progress_lockdown_english[year == "Year 4"], aes(colour = chapter_simple)) +
  facet_grid(. ~ level, scales = "free_x", drop = FALSE) +
  geom_rect(data = prop_change_sd[course == "English" & level != "Other" & year == "Year 4"][,level := factor(level)], aes(ymin = -2*sd, ymax = 2*sd), xmin = 0, xmax = 1000, fill = "grey90", colour = NA) +
  geom_rect(data = prop_change_sd[course == "English" & level != "Other" & year == "Year 4"][,level := factor(level)], aes(ymin = -sd, ymax = sd), xmin = 0, xmax = 100, fill = "grey75", colour = NA) +
  geom_hline(yintercept = 0, lty = 2) +
  geom_segment(aes(x = chapter_simple, xend = chapter_simple, y = (prop_change * 100)), yend = 0, alpha = .75) +
  geom_point(aes(x = chapter_simple, y = (prop_change * 100)), alpha = .75) +
  scale_y_continuous(breaks = c(-10, 0, 10), limits = c(-20, 20), labels = scales::number_format(suffix = " pp")) +
  scale_colour_viridis_d(direction = -1, na.translate = FALSE) +
  guides(colour = FALSE) +
  labs(x = NULL,
       y = "Change") +
  theme_paper +
  theme(panel.grid.major.y = element_blank(),
        strip.background = element_blank(),
        strip.text.x = element_blank(),
        panel.background = element_rect(fill = "transparent", colour = NA),
        plot.background = element_rect(fill = "transparent", colour = NA))

filler_plot <- qplot() + 
  theme_nothing() + 
  theme(panel.background = element_rect(fill = "transparent", colour = NA),
        plot.background = element_rect(fill = "transparent", colour = NA))

plot_grid(
          filler_plot,
          p_english_y1, filler_plot, p_change_english_y1, filler_plot,
          p_english_y2, filler_plot, p_change_english_y2, filler_plot, 
          p_english_y3, filler_plot, p_change_english_y3, filler_plot,
          p_english_y4, filler_plot, p_change_english_y4,
          ncol = 1,
          align = "hv", axis = "tblr",
          labels = c(NA,
                     "Year 1", NA, NA, NA,
                     "Year 2", NA, NA, NA,
                     "Year 3", NA, NA, NA,
                     "Year 4", NA, NA),
          rel_heights = c(.1, 
                          1, -.2, .75, .1,
                          1, -.2, .75, .1,
                          1, -.2, .75, .1,
                          1, -.2, .75),
          hjust = -0.1,
          vjust = -0.1,
          scale = .95)
Warning: Removed 61 rows containing missing values (position_stack).
Warning: Removed 61 rows containing missing values (geom_col).
Warning: Removed 69 rows containing missing values (position_stack).
Warning: Removed 60 rows containing missing values (geom_col).
Warning: Removed 49 rows containing missing values (position_stack).
Warning: Removed 50 rows containing missing values (geom_col).
Warning: Removed 7 rows containing missing values (position_stack).
Warning: Removed 4 rows containing missing values (geom_col).
Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

ggsave("../output/progress_combi_alt_english.pdf", width = 9, height = 11)
Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).
ggsave("../output/progress_combi_alt_english.eps", width = 9, height = 11)
Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).
Warning in grid.Call.graphics(C_rect, x$x, x$y, x$width, x$height,
resolveHJust(x$just, : semi-transparency is not supported on this device:
reported only once per page
ggsave("../output/progress_combi_alt_english.png", width = 9, height = 11)
Warning: Removed 1 rows containing missing values (geom_text).
Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Warning: Removed 1 rows containing missing values (geom_text).

Session info

sessionInfo()
R version 3.6.3 (2020-02-29)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 18.04.5 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.7.1
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.7.1

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=nl_NL.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=nl_NL.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=nl_NL.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] grid      stats     graphics  grDevices utils     datasets  methods  
[8] base     

other attached packages:
[1] lmerTest_3.1-0    lme4_1.1-21       Matrix_1.2-18     cowplot_0.9.4    
[5] ggplot2_3.3.2     DBI_1.1.0         data.table_1.13.6

loaded via a namespace (and not attached):
 [1] tidyselect_0.2.5    xfun_0.21           purrr_0.3.2        
 [4] splines_3.6.3       lattice_0.20-41     colorspace_1.4-1   
 [7] vctrs_0.2.2         htmltools_0.3.6     viridisLite_0.3.0  
[10] yaml_2.2.0          blob_1.2.1          rlang_0.4.10       
[13] pillar_1.4.2        nloptr_1.2.1        glue_1.3.1         
[16] withr_2.3.0         bit64_0.9-7         plyr_1.8.4         
[19] lifecycle_0.1.0     stringr_1.4.0       munsell_0.5.0      
[22] gtable_0.3.0        evaluate_0.14       memoise_1.1.0      
[25] labeling_0.3        knitr_1.23          Rcpp_1.0.6         
[28] scales_1.0.0        jsonlite_1.6        bit_1.1-14         
[31] digest_0.6.19       stringi_1.4.3       dplyr_0.8.3        
[34] numDeriv_2016.8-1.1 tools_3.6.3         magrittr_1.5       
[37] tibble_2.1.3        RSQLite_2.2.0       crayon_1.3.4       
[40] tidyr_1.0.0         pkgconfig_2.0.2     MASS_7.3-51.4      
[43] ellipsis_0.3.0      ggridges_0.5.1      assertthat_0.2.1   
[46] minqa_1.2.4         rmarkdown_2.6       R6_2.4.0           
[49] boot_1.3-25         nlme_3.1-149        compiler_3.6.3     
LS0tCnRpdGxlOiAnU2xpbVN0YW1wZW4gUGVyZm9ybWFuY2UgRHVyaW5nIExvY2tkb3duJwphdXRob3I6ICJNYWFydGVuIHZhbiBkZXIgVmVsZGUiCmRhdGU6ICJMYXN0IHVwZGF0ZWQ6IGByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgZ2l0aHViX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICBodG1sX25vdGVib29rOgogICAgc21hcnQ6IG5vCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgojIFNldHVwCgpgYGB7cn0KbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KERCSSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkoZ3JpZCkKbGlicmFyeShsbWU0KQpsaWJyYXJ5KGxtZXJUZXN0KQoKU3lzLnNldGxvY2FsZSgiTENfVElNRSIsICJlbl9VUy5VVEYtOCIpICMgUHJpbnQgRW5nbGlzaCBkYXRlIGZvcm1hdAojIFN5cy5zZXRsb2NhbGUoIkxDX1RJTUUiLCAibmxfTkwuVVRGLTgiKSAjIFByaW50IER1dGNoIGRhdGUgZm9ybWF0CgpudW1iZXJfZm9ybWF0IDwtIHNjYWxlczo6bnVtYmVyX2Zvcm1hdChiaWcubWFyayA9ICIsIiwgZGVjaW1hbC5tYXJrID0gIi4iKSAjIFByaW50IEVuZ2xpc2ggbnVtYmVyIGZvcm1hdAojIG51bWJlcl9mb3JtYXQgPC0gc2NhbGVzOjpudW1iZXJfZm9ybWF0KGJpZy5tYXJrID0gIi4iLCBkZWNpbWFsLm1hcmsgPSAiLCIpICMgUHJpbnQgRHV0Y2ggbnVtYmVyIGZvcm1hdAoKdGhlbWVfcGFwZXIgPC0gdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxMikgKyAKICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3VyID0gImJsYWNrIiksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJncmV5OTIiKSkKYGBgCgpTY2hvb2wgY2xvc3VyZSBhbmQgb3BlbmluZyBkYXRlcwoKU291cmNlczoKCiAgLSBodHRwczovL3d3dy5yaWprc292ZXJoZWlkLm5sL2FjdHVlZWwvbmlldXdzLzIwMjAvMDMvMTUvYWFudnVsbGVuZGUtbWFhdHJlZ2VsZW4tb25kZXJ3aWpzLWhvcmVjYS1zcG9ydAogIC0gaHR0cHM6Ly93d3cucmlqa3NvdmVyaGVpZC5ubC9hY3R1ZWVsL25pZXV3cy8yMDIwLzA1LzE5L29uZGVyd2lqcy1nYWF0LXN0YXAtdm9vci1zdGFwLW9wZW4KCmBgYHtyfQpkYXRlX3NjaG9vbHNfY2xvc2VkIDwtIGFzLlBPU0lYY3QoIjIwMjAtMDMtMTYiKQpkYXRlX3NjaG9vbHNfb3BlbmVkIDwtIGFzLlBPU0lYY3QoIjIwMjAtMDYtMDIiKQpgYGAKCgpIYW5kbGUgZGF0YWJhc2UgY29ubmVjdGlvbnMKYGBge3J9CmRiX2Nvbm5lY3QgPC0gZnVuY3Rpb24oKSB7CiAgZGIgPC0gZGJDb25uZWN0KFJTUUxpdGU6OlNRTGl0ZSgpLCBmaWxlLnBhdGgoIi4uIiwgImRhdGEiLCAibm9vcmRob2ZmLnNxbGl0ZSIpKQogIHJldHVybihkYikKfQoKZGJfZGlzY29ubmVjdCA8LSBmdW5jdGlvbihkYikgewogIGRiRGlzY29ubmVjdChkYikKfQpgYGAKCgojIERhdGEKClRoZSBkYXRhYmFzZSBjb250YWlucyBhbGwgU2xpbVN0YW1wZW4gZGF0YSBjb2xsZWN0ZWQgdmlhIE5vb3JkaG9mZidzIHBsYXRmb3JtIGluIHRocmVlIGNvdXJzZXM6ICpTdGVwcGluZyBTdG9uZXMqIChFbmdsaXNoKSwgKkdyYW5kZXMgTGlnbmVzKiAoRnJlbmNoKSwgYW5kICpOZXVlIEtvbnRha3RlKiAoR2VybWFuKS4KClRyaWFsLWxldmVsIHJlc3BvbnNlIGRhdGEgYXJlIHN0b3JlZCBpbiB0aGUgYHJlc3BvbnNlc2AgdGFibGUuCkJvb2sgaW5mb3JtYXRpb24sIHN1Y2ggYXMgdGhlIGNvdXJzZSB5ZWFyLCBib29rIHRpdGxlLCBhbmQgY2hhcHRlciwgYXJlIHN0b3JlZCBpbiB0aGUgYGJvb2tfaW5mb2AgdGFibGUuCgojIyBgcmVzcG9uc2VzYAoKfCBDb2x1bW4gICAgICAgICAgICAgICB8IFR5cGUgICAgICB8IEV4cGxhbmF0aW9uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfAp8IGRhdGUgICAgICAgICAgICAgICAgIHwgaW50ICAgICAgIHwgVU5JWCB0aW1lIHN0YW1wIFtzXSAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCB1c2VyX2lkICAgICAgICAgICAgICB8IGNociAgICAgICB8IHVuaXF1ZSB1c2VyIGlkZW50aWZpZXIgICAgICAgICAgICAgICAgICAgICAgICB8CnwgbWV0aG9kICAgICAgICAgICAgICAgfCBjaHIgICAgICAgfCBjb3Vyc2UgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IHN0YXJ0X3RpbWUgICAgICAgICAgIHwgaW50ICAgICAgIHwgZWxhcHNlZCB0aW1lIHNpbmNlIHNlc3Npb24gc3RhcnQgW21zXSAgICAgICAgIHwKfCBydCAgICAgICAgICAgICAgICAgICB8IGludCAgICAgICB8IHJlc3BvbnNlIHRpbWUgW21zXSAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgZHVyYXRpb24gICAgICAgICAgICAgfCBpbnQgICAgICAgfCB0cmlhbCBkdXJhdGlvbiBbbXNdICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGZhY3RfaWQgICAgICAgICAgICAgIHwgaW50ICAgICAgIHwgdW5pcXVlIGZhY3QgaWRlbnRpZmllciAod2l0aGluIGNoYXB0ZXIpICAgICAgIHwKfCBjb3JyZWN0ICAgICAgICAgICAgICB8IGludCAgICAgICB8IHJlc3BvbnNlIGFjY3VyYWN5ICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgYW5zd2VyICAgICAgICAgICAgICAgfCBjaHIgICAgICAgfCB1c2VyJ3MgcmVzcG9uc2UgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGNob2ljZXMgICAgICAgICAgICAgIHwgaW50ICAgICAgIHwgbnVtYmVyIG9mIGFuc3dlciBjaG9pY2VzICgxID09IG9wZW4gcmVzcG9uc2UpIHwKfCBiYWNrc3BhY2VfdXNlZCAgICAgICB8IGRibCAgICAgICB8IHVzZXIgcHJlc3NlZCBiYWNrc3BhY2UgZHVyaW5nIHRyaWFsICAgICAgICAgICB8CnwgYmFja3NwYWNlX3VzZWRfZmlyc3QgfCBkYmwgICAgICAgfCB1c2VyIGVyYXNlZCBmaXJzdCBjaGFyYWN0ZXIgb2YgcmVzcG9uc2UgICAgICAgfAp8IHN0dWR5ICAgICAgICAgICAgICAgIHwgaW50ICAgICAgIHwgdHJpYWwgd2FzIGEgc3R1ZHkgdHJpYWwgICAgICAgICAgICAgICAgICAgICAgIHwKfCBhbnN3ZXJfbGFuZ3VhZ2UgICAgICB8IGNociAgICAgICB8IGxhbmd1YWdlIG9mIHRoZSBhbnN3ZXIgICAgICAgICAgICAgICAgICAgICAgICB8Cnwgc3Vic2Vzc2lvbiAgICAgICAgICAgfCBpbnQgICAgICAgfCBpZGVudGlmaWVzIHBhcnQgd2l0aGluIGxlYXJuaW5nIHNlc3Npb24gICAgICAgfAp8IGJvb2tfaW5mb19pZCAgICAgICAgIHwgY2hyICAgICAgIHwgdW5pcXVlIGlkZW50aWZpZXIgb2YgYm9vayBpbmZvcm1hdGlvbiAgICAgICAgIHwKCgojIyBgYm9va19pbmZvYAoKfCBDb2x1bW4gICAgICAgICAgICAgICB8IFR5cGUgICAgICB8IEV4cGxhbmF0aW9uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfAp8IGJvb2tfaW5mb19pZCAgICAgICAgIHwgY2hyICAgICAgIHwgdW5pcXVlIGlkZW50aWZpZXIgb2YgYm9vayBpbmZvcm1hdGlvbiAgICAgICAgIHwKfCBtZXRob2RfZ3JvdXAgICAgICAgICB8IGNociAgICAgICB8IHllYXIgYW5kIGVkaXRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgYm9va190aXRsZSAgICAgICAgICAgfCBjaHIgICAgICAgfCBib29rIHRpdGxlIChpbmNsLiB5ZWFyLCBsZXZlbCwgZWRpdGlvbikgICAgICAgfAp8IGJvb2tfdHlwZSAgICAgICAgICAgIHwgY2hyICAgICAgIHwgdHlwZSBvZiBib29rICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBjaGFwdGVyICAgICAgICAgICAgICB8IGNociAgICAgICB8IGNoYXB0ZXIgbnVtYmVyIGFuZCB0aXRsZSAgICAgICAgICAgICAgICAgICAgICB8CgoKUHJldmlldyBmaXJzdCAxMCByb3dzCmBgYHtyfQpkYiA8LSBkYl9jb25uZWN0KCkKcmVzcG9uc2VzX3RvcCA8LSBkYkdldFF1ZXJ5KGRiLCAiU0VMRUNUICogRlJPTSByZXNwb25zZXNfbm9kdXBsaWNhdGVzIExJTUlUIDEwIikKcmVzcG9uc2VzX3RvcAoKYm9va19pbmZvX3RvcCA8LSBkYkdldFF1ZXJ5KGRiLCAiU0VMRUNUICogRlJPTSBib29rX2luZm8gTElNSVQgMTAiKQpib29rX2luZm9fdG9wCmRiX2Rpc2Nvbm5lY3QoZGIpCmBgYAoKCiMgUGVyZm9ybWFuY2UKClRoZXJlIGFyZSBzZXZlcmFsIG1lYXN1cmVzIG9mIGxlYXJuaW5nIHBlcmZvcm1hbmNlIHdlIGNhbiBsb29rIGF0LgpUaGUgbW9zdCBzdHJhaWdodC1mb3J3YXJkIG9mIHRoZXNlIGFyZSByZXNwb25zZSBhY2N1cmFjeSBhbmQgcmVzcG9uc2UgdGltZS4KCkltcG9ydGFudCBmYWN0b3JzIHRvIGtlZXAgaW4gbWluZDogcXVlc3Rpb24gdHlwZSAobXVsdGlwbGUgY2hvaWNlIG9yIG9wZW4gYW5zd2VyKSBhbmQgbGFuZ3VhZ2UuCk5vdGUgdGhhdCB3ZSBjYW5ub3QgZGlzdGluZ3Vpc2ggYmV0d2VlbiBOTC1YIGFuZCBYLVgsIHNpbmNlIHdlIG9ubHkga25vdyB0aGUgbGFuZ3VhZ2Ugb2YgdGhlIGFuc3dlci4KCiMjIFJlc3BvbnNlIGFjY3VyYWN5CgojIyMgV2hvbGUgcG9wdWxhdGlvbgoKYGBge3J9CmRiIDwtIGRiX2Nvbm5lY3QoKQpjb3JyZWN0IDwtIGRiR2V0UXVlcnkoZGIsIAogICAgICAgICAgICAgICAgICAgICAgIlNFTEVDVCByLm1ldGhvZCBBUyAnbWV0aG9kJywKICAgICAgICAgICAgICAgICAgICAgIERBVEUoci5kYXRlICsgMzYwMCwgJ3VuaXhlcG9jaCcpIEFTICdkb3knLAogICAgICAgICAgICAgICAgICAgICAgci51c2VyX2lkIEFTICd1c2VyJywKICAgICAgICAgICAgICAgICAgICAgIHIuY2hvaWNlcyA+IDEgQVMgJ21jcScsCiAgICAgICAgICAgICAgICAgICAgICByLmNvcnJlY3QgQVMgJ2NvcnJlY3QnLAogICAgICAgICAgICAgICAgICAgICAgQ09VTlQoKikgQVMgJ24nCiAgICAgICAgICAgICAgICAgICAgICBGUk9NICdyZXNwb25zZXNfbm9kdXBsaWNhdGVzJyByCiAgICAgICAgICAgICAgICAgICAgICBXSEVSRSByLnN0dWR5ID09IDAKICAgICAgICAgICAgICAgICAgICAgIEdST1VQIEJZIHIubWV0aG9kLAogICAgICAgICAgICAgICAgICAgICAgREFURShyLmRhdGUgKyAzNjAwLCAndW5peGVwb2NoJyksCiAgICAgICAgICAgICAgICAgICAgICByLnVzZXJfaWQsCiAgICAgICAgICAgICAgICAgICAgICByLmNob2ljZXMgPiAxLAogICAgICAgICAgICAgICAgICAgICAgci5jb3JyZWN0IgopCnNldERUKGNvcnJlY3QpCmRiX2Rpc2Nvbm5lY3QoZGIpCmBgYAoKRmlsbCBpbiBtaXNzaW5nIHJvd3MgKHdoZXJlIGFsbCB0cmlhbHMgb24gYSBkYXkgd2VyZSBjb3JyZWN0L2luY29ycmVjdCk6CmBgYHtyfQpjb3JyZWN0IDwtIHRpZHlyOjpjb21wbGV0ZShjb3JyZWN0LCB0aWR5cjo6bmVzdGluZyhtZXRob2QsIGRveSwgdXNlciwgbWNxKSwgY29ycmVjdCwgZmlsbCA9IGxpc3QobiA9IDApKQpzZXREVChjb3JyZWN0KQpgYGAKCmBgYHtyfQpjb3JyZWN0WywgbWNxIDo9IGFzLmxvZ2ljYWwobWNxKV0KYGBgCgpgYGB7cn0KYWNjdXJhY3kgPC0gY29ycmVjdFssIC4oYWNjdXJhY3kgPSBuW2NvcnJlY3QgPT0gMV0vc3VtKG4pLCBuID0gc3VtKG4pKSwgYnkgPSAuKG1ldGhvZCwgZG95LCB1c2VyLCBtY3EpXQpgYGAKCkFkZCBhIHNjaG9vbCB5ZWFyIGNvbHVtbiAoY3V0b2ZmIGRhdGU6IDEgQXVndXN0KToKYGBge3J9CmFjY3VyYWN5WywgZG95X3Bvc2l4IDo9IGFzLlBPU0lYY3QoZG95KV0KYWNjdXJhY3lbLCBzY2hvb2xfeWVhciA6PSBpZmVsc2UoZG95X3Bvc2l4IDwgIjIwMTktMDgtMDEiLCAiMTgvMTkiLCAiMTkvMjAiKV0KYGBgCgpBZGQgc2Vuc2libGUgY291cnNlIG5hbWVzOgpgYGB7cn0KYWNjdXJhY3lbLCBjb3Vyc2UgOj0gaWZlbHNlKG1ldGhvZCA9PSAiR3JhbmRlcyBMaWduZXMiLCAiRnJlbmNoIiwgaWZlbHNlKG1ldGhvZCA9PSAiU3RlcHBpbmcgU3RvbmVzIiwgIkVuZ2xpc2giLCAiR2VybWFuIikpXQpgYGAKCkFsaWduIHNjaG9vbCB5ZWFyczoKYGBge3J9CmFjY3VyYWN5W3NjaG9vbF95ZWFyID09ICIxOC8xOSIsIGRveV9wb3NpeF9hbGlnbmVkIDo9IGFzLlBPU0lYY3QoZG95X3Bvc2l4ICsgMzY1KjI0KjYwKjYwLCBvcmlnaW4gPSAiMTk3MC0wMS0wMSIpXQphY2N1cmFjeVtzY2hvb2xfeWVhciA9PSAiMTkvMjAiLCBkb3lfcG9zaXhfYWxpZ25lZCA6PSBkb3lfcG9zaXhdCmBgYAoKVXNlIGN1dC5EYXRlKCkgdG8gYmluIGRhdGVzIGJ5IHdlZWsuIEVhY2ggZGF5IGlzIGFzc2lnbmVkIHRoZSBkYXRlIG9mIHRoZSBtb3N0IHJlY2VudCBNb25kYXkuCmBgYHtyfQphY2N1cmFjeVssIGRveV9wb3NpeF93ZWVrIDo9IGN1dC5QT1NJWHQoZG95X3Bvc2l4LCAid2VlayIpXQphY2N1cmFjeVssIGRveV9wb3NpeF9hbGlnbmVkX3dlZWsgOj0gY3V0LlBPU0lYdChkb3lfcG9zaXhfYWxpZ25lZCwgIndlZWsiKV0KYGBgCgpgYGB7cn0KYWNjdXJhY3lfYnlfd2Vla19hbmRfdXNlciA8LSBhY2N1cmFjeVssIC4oYWNjdXJhY3kgPSBzdW0oYWNjdXJhY3kqbikvc3VtKG4pKSwgYnkgPSAuKGNvdXJzZSwgc2Nob29sX3llYXIsIGRveV9wb3NpeF9hbGlnbmVkX3dlZWssIHVzZXIsIG1jcSldCmFjY3VyYWN5X2J5X3dlZWsgPC0gYWNjdXJhY3lfYnlfd2Vla19hbmRfdXNlclssIC4oYWNjdXJhY3lfbWVhbiA9IG1lYW4oYWNjdXJhY3ksIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjY3VyYWN5X3NlID0gc2QoYWNjdXJhY3ksIG5hLnJtID0gVFJVRSkvc3FydCguTiksIG4gPSAuTiksIGJ5ID0gLihjb3Vyc2UsIHNjaG9vbF95ZWFyLCBkb3lfcG9zaXhfYWxpZ25lZF93ZWVrLCBtY3EpXQpgYGAKCgpBZGQgcXVlc3Rpb24gdHlwZSBsYWJlbHM6CmBgYHtyfQphY2N1cmFjeV9ieV93ZWVrWywgcXVlc3Rpb25fdHlwZSA6PSBpZmVsc2UobWNxID09IFRSVUUsICJNdWx0aXBsZVxuY2hvaWNlIiwgIk9wZW5cbmFuc3dlciIpXQpgYGAKCgpQbG90IHJlc3BvbnNlIGFjY3VyYWN5IGJ5IHdlZWsgKG1lYW4gKy8tIDEgU0UpLgpgYGB7cn0KcF9hY2MgPC0gZ2dwbG90KGFjY3VyYWN5X2J5X3dlZWtbKGNvdXJzZSA9PSAiRW5nbGlzaCIgJiBtY3EgPT0gVFJVRSkgfCBjb3Vyc2UgPT0gIkZyZW5jaCIsXSwKICAgICAgICAgICAgYWVzKHggPSBhcy5QT1NJWGN0KGRveV9wb3NpeF9hbGlnbmVkX3dlZWspLCB5ID0gYWNjdXJhY3lfbWVhbiwgZ3JvdXAgPSBpbnRlcmFjdGlvbihzY2hvb2xfeWVhciwgcXVlc3Rpb25fdHlwZSksIGNvbG91ciA9IHNjaG9vbF95ZWFyLCBmaWxsID0gc2Nob29sX3llYXIpKSArCiAgZmFjZXRfZ3JpZCguIH4gY291cnNlKSArCiAgZ2VvbV9yZWN0KHhtaW4gPSBkYXRlX3NjaG9vbHNfY2xvc2VkLCB4bWF4ID0gZGF0ZV9zY2hvb2xzX29wZW5lZCwgeW1pbiA9IDAsIHltYXggPSAxLjA1LCBmaWxsID0gImdyZXk5MiIsIGNvbG91ciA9ICJncmV5NTAiLCBsdHkgPSAyLCBhbHBoYSA9IC45KSArCiAgZ2VvbV9yaWJib24oYWVzKHltaW4gPSBhY2N1cmFjeV9tZWFuIC0gYWNjdXJhY3lfc2UsIHltYXggPSBhY2N1cmFjeV9tZWFuICsgYWNjdXJhY3lfc2UsIGNvbG91ciA9IE5VTEwpLCBhbHBoYSA9IDAuMikgKwogIGdlb21fbGluZShhZXMobHR5ID0gcXVlc3Rpb25fdHlwZSkpICsKICBzY2FsZV94X2RhdGV0aW1lKGV4cGFuZCA9IGMoMCwgMCksCiAgICAgICAgICAgICAgICAgICBicmVha3MgPSBhcy5QT1NJWGN0KGMoCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTEwLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTEyLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTAyLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA0LTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA2LTAxIDAyOjAwOjAwIENFVCIpKSwKICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGFzLlBPU0lYY3QoYygiMjAxOS0wOS0wMSAwMjowMDowMCBDRVQiLCAiMjAyMC0wNy0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYyguNywgMSksIGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoYWNjdXJhY3kgPSAxKSkgKwogIHNjYWxlX2NvbG91cl92aXJpZGlzX2QoZW5kID0gLjUsIGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKGVuZCA9IC41LCBkaXJlY3Rpb24gPSAtMSwgbmEudHJhbnNsYXRlID0gRkFMU0UpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJBY2N1cmFjeSIsCiAgICAgICBjb2xvdXIgPSAiU2Nob29sIHllYXIiLAogICAgICAgZmlsbCA9ICJTY2hvb2wgeWVhciIsCiAgICAgICBsdHkgPSAiUXVlc3Rpb24gdHlwZSIpICsKICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMSksCiAgICAgICAgIGZpbGwgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxKSwKICAgICAgICAgbHR5ID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMikpICsKICB0aGVtZV9wYXBlcgpwX2FjYwpnZ3NhdmUoIi4uL291dHB1dC9hY2NfYnlfcXVlc3Rpb25fdHlwZS5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDMpCmdnc2F2ZSgiLi4vb3V0cHV0L2FjY19ieV9xdWVzdGlvbl90eXBlLmVwcyIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gMykKZ2dzYXZlKCIuLi9vdXRwdXQvYWNjX2J5X3F1ZXN0aW9uX3R5cGUucG5nIiwgd2lkdGggPSA5LCBoZWlnaHQgPSAzKQpgYGAKCgojIyMgQnkgbGV2ZWwgYW5kIHllYXIKCmBgYHtyfQpkYiA8LSBkYl9jb25uZWN0KCkKY29ycmVjdF9zdHJhdCA8LSBkYkdldFF1ZXJ5KGRiLCAKICAgICAgICAgICAgICAgICAgICAgICJTRUxFQ1Qgci5tZXRob2QgQVMgJ21ldGhvZCcsCiAgICAgICAgICAgICAgICAgICAgICByLmJvb2tfaW5mb19pZCBBUyAnYm9va19pbmZvX2lkJywKICAgICAgICAgICAgICAgICAgICAgIERBVEUoci5kYXRlICsgMzYwMCwgJ3VuaXhlcG9jaCcpIEFTICdkb3knLAogICAgICAgICAgICAgICAgICAgICAgci51c2VyX2lkIEFTICd1c2VyJywKICAgICAgICAgICAgICAgICAgICAgIHIuY2hvaWNlcyA+IDEgQVMgJ21jcScsCiAgICAgICAgICAgICAgICAgICAgICByLmNvcnJlY3QgQVMgJ2NvcnJlY3QnLAogICAgICAgICAgICAgICAgICAgICAgQ09VTlQoKikgQVMgJ24nCiAgICAgICAgICAgICAgICAgICAgICBGUk9NICdyZXNwb25zZXNfbm9kdXBsaWNhdGVzJyByCiAgICAgICAgICAgICAgICAgICAgICBXSEVSRSByLnN0dWR5ID09IDAKICAgICAgICAgICAgICAgICAgICAgIEdST1VQIEJZIHIubWV0aG9kLAogICAgICAgICAgICAgICAgICAgICAgci5ib29rX2luZm9faWQsCiAgICAgICAgICAgICAgICAgICAgICBEQVRFKHIuZGF0ZSArIDM2MDAsICd1bml4ZXBvY2gnKSwKICAgICAgICAgICAgICAgICAgICAgIHIudXNlcl9pZCwKICAgICAgICAgICAgICAgICAgICAgIHIuY2hvaWNlcyA+IDEsCiAgICAgICAgICAgICAgICAgICAgICByLmNvcnJlY3QiCikKc2V0RFQoY29ycmVjdF9zdHJhdCkKZGJfZGlzY29ubmVjdChkYikKYGBgCgpGaWxsIGluIG1pc3Npbmcgcm93cyAod2hlcmUgYWxsIHRyaWFscyBvbiBhIGRheSB3ZXJlIGNvcnJlY3QvaW5jb3JyZWN0KToKYGBge3J9CmNvcnJlY3Rfc3RyYXQgPC0gdGlkeXI6OmNvbXBsZXRlKGNvcnJlY3Rfc3RyYXQsIHRpZHlyOjpuZXN0aW5nKG1ldGhvZCwgYm9va19pbmZvX2lkLCBkb3ksIHVzZXIsIG1jcSksIGNvcnJlY3QsIGZpbGwgPSBsaXN0KG4gPSAwKSkKc2V0RFQoY29ycmVjdF9zdHJhdCkKYGBgCgpgYGB7cn0KY29ycmVjdF9zdHJhdFssIG1jcSA6PSBhcy5sb2dpY2FsKG1jcSldCmBgYAoKQWRkIGJvb2sgaW5mb3JtYXRpb246CmBgYHtyfQpkYiA8LSBkYl9jb25uZWN0KCkKYm9va19pbmZvIDwtIGRiR2V0UXVlcnkoZGIsICJTRUxFQ1QgRElTVElOQ1QgKiBGUk9NICdib29rX2luZm8nIikKZGJfZGlzY29ubmVjdChkYikKCnNldERUKGJvb2tfaW5mbykKYGBgCgpgYGB7cn0KY29ycmVjdF9zdHJhdFtib29rX2luZm9bYm9va190eXBlID09ICJIb29mZGJvZWsiLF0sIG9uICA9ICJib29rX2luZm9faWQiLCBjKCJtZXRob2RfZ3JvdXAiLCAiYm9va190aXRsZSIpIDo9IC4oaS5tZXRob2RfZ3JvdXAsIGkuYm9va190aXRsZSldCmBgYAoKQWRkIHNlbnNpYmxlIGNvdXJzZSBuYW1lczoKYGBge3J9CmNvcnJlY3Rfc3RyYXRbLCBjb3Vyc2UgOj0gaWZlbHNlKG1ldGhvZCA9PSAiR3JhbmRlcyBMaWduZXMiLCAiRnJlbmNoIiwgaWZlbHNlKG1ldGhvZCA9PSAiU3RlcHBpbmcgU3RvbmVzIiwgIkVuZ2xpc2giLCAiR2VybWFuIikpXQpgYGAKCkFkZCBhIHNjaG9vbCB5ZWFyIGNvbHVtbiAoY3V0b2ZmIGRhdGU6IDEgQXVndXN0KToKYGBge3J9CmNvcnJlY3Rfc3RyYXRbLCBkb3lfcG9zaXggOj0gYXMuUE9TSVhjdChkb3kpXQpjb3JyZWN0X3N0cmF0Wywgc2Nob29sX3llYXIgOj0gaWZlbHNlKGRveV9wb3NpeCA8ICIyMDE5LTA4LTAxIiwgIjE4LzE5IiwgIjE5LzIwIildCmBgYAoKU2ltcGxpZnkgbGV2ZWwgbmFtZXM6CmBgYHtyfQojIEtlZXAgYWxsIGRpc3RpbmN0aW9ucwpjb3JyZWN0X3N0cmF0WywgYm9va190aXRsZV9zaW1wbGUgOj0gc3RyaW5ncjo6c3RyX3N1Yihib29rX3RpdGxlLCAzLCAtMTApXQpjb3JyZWN0X3N0cmF0WywgYm9va190aXRsZV9zaW1wbGUgOj0gZmFjdG9yKGJvb2tfdGl0bGVfc2ltcGxlLCBsZXZlbHMgPSBjKCJ2bWJvIGIvbHdvbyIsICJ2bWJvIGIiLCAidm1ibyBiayIsICJ2bWJvIGsiLCAidm1ibyBrZ3QiLCAidm1iby1ndCIsICJ2bWJvIGd0IiwgInZtYm8tZ3QvaGF2byIsICJ2bWJvICh0KWh2IiwgImhhdm8iLCAiaGF2byB2d28iLCAidndvIikpXQoKIyBTaW1wbGlmeSB0byB0aHJlZSBsZXZlbHMKY29ycmVjdF9zdHJhdFssIGxldmVsIDo9IGRwbHlyOjpjYXNlX3doZW4oCiAgZ3JlcGwoICJodiIsIGJvb2tfdGl0bGUpIH4gIkdlbmVyYWwgc2Vjb25kYXJ5XG4oaGF2bykiLAogIGdyZXBsKCJ2bWJvIiwgYm9va190aXRsZSkgfiAiUHJlLXZvY2F0aW9uYWxcbih2bWJvKSIsCiAgZ3JlcGwoImhhdm8iLCBib29rX3RpdGxlKSB+ICJHZW5lcmFsIHNlY29uZGFyeVxuKGhhdm8pIiwKICBncmVwbCgidndvIiwgYm9va190aXRsZSkgfiAiUHJlLXVuaXZlcnNpdHlcbih2d28pIiwKICBUUlVFIH4gIk90aGVyIildCmNvcnJlY3Rfc3RyYXRbLCBsZXZlbCA6PSBmYWN0b3IobGV2ZWwsIGxldmVscyA9IGMoIk90aGVyIiwgIlByZS12b2NhdGlvbmFsXG4odm1ibykiLCAiR2VuZXJhbCBzZWNvbmRhcnlcbihoYXZvKSIsICJQcmUtdW5pdmVyc2l0eVxuKHZ3bykiKSldCmBgYAoKU2ltcGxpZnkgeWVhciBuYW1lczoKYGBge3J9CmNvcnJlY3Rfc3RyYXRbLCB5ZWFyIDo9IGRwbHlyOjpjYXNlX3doZW4oCiAgbWV0aG9kX2dyb3VwID09ICJMZWVyamFhciAxICg1ZSBFZC4pIiB+ICJZZWFyIDEiLAogIG1ldGhvZF9ncm91cCA9PSAiTGVlcmphYXIgMiAoNWUgRWQuKSIgfiAiWWVhciAyIiwKICBtZXRob2RfZ3JvdXAgPT0gIkxlZXJqYWFyIDMgKDVlIEVkLikiIH4gIlllYXIgMyIsCiAgbWV0aG9kX2dyb3VwID09ICJMZWVyamFhciAzLzQgKDVlIEVkLikiIH4gIlllYXIgMy80IiwKICBtZXRob2RfZ3JvdXAgPT0gIkxlZXJqYWFyIDQgKDVlIEVkLikiIH4gIlllYXIgNCIsCiAgbWV0aG9kX2dyb3VwID09ICJUd2VlZGUgRmFzZSAoNmUgRWQuKSIgfiAiVHdlZWRlIEZhc2UiLAogIFRSVUUgfiAiT3RoZXIiKV0KYGBgCgpDb25zb2xpZGF0ZSBieSBkYXk6CmBgYHtyfQphY2N1cmFjeV9zdHJhdCA8LSBjb3JyZWN0X3N0cmF0WywgLihhY2N1cmFjeSA9IG5bY29ycmVjdCA9PSAxXS9zdW0obiksIG4gPSBzdW0obikpLCBieSA9IC4oc2Nob29sX3llYXIsIGRveV9wb3NpeCwgY291cnNlLCBsZXZlbCwgeWVhciwgdXNlciwgbWNxKV0KYGBgCgpBbGlnbiBzY2hvb2wgeWVhcnM6CmBgYHtyfQphY2N1cmFjeV9zdHJhdFtzY2hvb2xfeWVhciA9PSAiMTgvMTkiLCBkb3lfcG9zaXhfYWxpZ25lZCA6PSBhcy5QT1NJWGN0KGRveV9wb3NpeCArIDM2NSoyNCo2MCo2MCwgb3JpZ2luID0gIjE5NzAtMDEtMDEiKV0KYWNjdXJhY3lfc3RyYXRbc2Nob29sX3llYXIgPT0gIjE5LzIwIiwgZG95X3Bvc2l4X2FsaWduZWQgOj0gZG95X3Bvc2l4XQpgYGAKClVzZSBjdXQuRGF0ZSgpIHRvIGJpbiBkYXRlcyBieSB3ZWVrLiBFYWNoIGRheSBpcyBhc3NpZ25lZCB0aGUgZGF0ZSBvZiB0aGUgbW9zdCByZWNlbnQgTW9uZGF5LgpgYGB7cn0KYWNjdXJhY3lfc3RyYXRbLCBkb3lfcG9zaXhfd2VlayA6PSBjdXQuUE9TSVh0KGRveV9wb3NpeCwgIndlZWsiKV0KYWNjdXJhY3lfc3RyYXRbLCBkb3lfcG9zaXhfYWxpZ25lZF93ZWVrIDo9IGN1dC5QT1NJWHQoZG95X3Bvc2l4X2FsaWduZWQsICJ3ZWVrIildCmBgYAoKYGBge3J9CmFjY3VyYWN5X3N0cmF0X2J5X3dlZWtfYW5kX3VzZXIgPC0gYWNjdXJhY3lfc3RyYXRbLCAuKGFjY3VyYWN5ID0gc3VtKGFjY3VyYWN5Km4pL3N1bShuKSksIGJ5ID0gLihjb3Vyc2UsIHNjaG9vbF95ZWFyLCBkb3lfcG9zaXhfYWxpZ25lZF93ZWVrLCBsZXZlbCwgeWVhciwgdXNlciwgbWNxKV0KYWNjdXJhY3lfc3RyYXRfYnlfd2VlayA8LSBhY2N1cmFjeV9zdHJhdF9ieV93ZWVrX2FuZF91c2VyWywgLihhY2N1cmFjeV9tZWFuID0gbWVhbihhY2N1cmFjeSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWNjdXJhY3lfc2UgPSBzZChhY2N1cmFjeSwgbmEucm0gPSBUUlVFKS9zcXJ0KC5OKSwgbiA9IC5OKSwgYnkgPSAuKGNvdXJzZSwgc2Nob29sX3llYXIsIGRveV9wb3NpeF9hbGlnbmVkX3dlZWssIGxldmVsLCB5ZWFyLCBtY3EpXQpgYGAKCkFkZCBxdWVzdGlvbiB0eXBlIGxhYmVsczoKYGBge3J9CmFjY3VyYWN5X3N0cmF0X2J5X3dlZWtfYW5kX3VzZXJbLCBxdWVzdGlvbl90eXBlIDo9IGlmZWxzZShtY3EgPT0gVFJVRSwgIk11bHRpcGxlXG5jaG9pY2UiLCAiT3BlblxuYW5zd2VyIildCmFjY3VyYWN5X3N0cmF0X2J5X3dlZWtbLCBxdWVzdGlvbl90eXBlIDo9IGlmZWxzZShtY3EgPT0gVFJVRSwgIk11bHRpcGxlXG5jaG9pY2UiLCAiT3BlblxuYW5zd2VyIildCmBgYAoKSG93IG1hbnkgdW5pcXVlIHVzZXJzIHBlciBncm91cD8KYGBge3J9CmFjY3VyYWN5X3N0cmF0X2J5X3dlZWtfYW5kX3VzZXJbLCAuKHVuaXF1ZV91c2VycyA9IGxlbmd0aCh1bmlxdWUodXNlcikpKSwgIGJ5ID0gLihjb3Vyc2UsIGxldmVsLCB5ZWFyLCBzY2hvb2xfeWVhciwgcXVlc3Rpb25fdHlwZSldCmBgYAoKClBsb3QgcmVzcG9uc2UgYWNjdXJhY3kgYnkgd2VlayAobWVhbiArLy0gMSBTRSkuCmBgYHtyfQpwX2FjY19sZXZlbF95ZWFyIDwtIGdncGxvdChhY2N1cmFjeV9zdHJhdF9ieV93ZWVrW2NvdXJzZSA9PSAiRnJlbmNoIiAmIGxldmVsICE9ICJPdGhlciIsXSwKICAgICAgICAgICAgYWVzKHggPSBhcy5QT1NJWGN0KGRveV9wb3NpeF9hbGlnbmVkX3dlZWspLCB5ID0gYWNjdXJhY3lfbWVhbiwgZ3JvdXAgPSBpbnRlcmFjdGlvbihzY2hvb2xfeWVhciwgcXVlc3Rpb25fdHlwZSksIGNvbG91ciA9IHNjaG9vbF95ZWFyLCBmaWxsID0gc2Nob29sX3llYXIpKSArCiAgZmFjZXRfZ3JpZChsZXZlbCB+IHllYXIpICsKICBnZW9tX3JlY3QoeG1pbiA9IGRhdGVfc2Nob29sc19jbG9zZWQsIHhtYXggPSBkYXRlX3NjaG9vbHNfb3BlbmVkLCB5bWluID0gMCwgeW1heCA9IDEuMDUsIGZpbGwgPSAiZ3JleTkyIiwgY29sb3VyID0gImdyZXk1MCIsIGx0eSA9IDIsIGFscGhhID0gLjkpICsKICBnZW9tX3JpYmJvbihhZXMoeW1pbiA9IGFjY3VyYWN5X21lYW4gLSBhY2N1cmFjeV9zZSwgeW1heCA9IGFjY3VyYWN5X21lYW4gKyBhY2N1cmFjeV9zZSwgY29sb3VyID0gTlVMTCksIGFscGhhID0gMC4yKSArCiAgZ2VvbV9saW5lKGFlcyhsdHkgPSBxdWVzdGlvbl90eXBlKSkgKwogIHNjYWxlX3hfZGF0ZXRpbWUoZXhwYW5kID0gYygwLCAwKSwKICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGFzLlBPU0lYY3QoYygKICAgICAgICAgICAgICAgICAgICAgIjIwMTktMTAtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMTktMTItMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDItMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDQtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDYtMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgbGltaXRzID0gYXMuUE9TSVhjdChjKCIyMDE5LTA5LTAxIDAyOjAwOjAwIENFVCIsICIyMDIwLTA3LTAxIDAyOjAwOjAwIENFVCIpKSwKICAgICAgICAgICAgICAgICAgIGRhdGVfbGFiZWxzID0gIiViIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KGFjY3VyYWN5ID0gMSkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoLjQsIDEpKSArCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXNfZChlbmQgPSAuNSwgZGlyZWN0aW9uID0gLTEsIG5hLnRyYW5zbGF0ZSA9IEZBTFNFKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoZW5kID0gLjUsIGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gIkFjY3VyYWN5IiwKICAgICAgIGNvbG91ciA9ICJTY2hvb2wgeWVhciIsCiAgICAgICBmaWxsID0gIlNjaG9vbCB5ZWFyIiwKICAgICAgIGx0eSA9ICJRdWVzdGlvbiB0eXBlIikgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxKSwKICAgICAgICAgZmlsbCA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEpLAogICAgICAgICBsdHkgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAyKSkgKwogIHRoZW1lX3BhcGVyCgpwX2FjY19sZXZlbF95ZWFyCgpnZ3NhdmUoIi4uL291dHB1dC9hY2NfYnlfcXVlc3Rpb25fdHlwZV9mcmVuY2hfbGV2ZWxfeWVhci5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDUpCmdnc2F2ZSgiLi4vb3V0cHV0L2FjY19ieV9xdWVzdGlvbl90eXBlX2ZyZW5jaF9sZXZlbF95ZWFyLmVwcyIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gNSkKZ2dzYXZlKCIuLi9vdXRwdXQvYWNjX2J5X3F1ZXN0aW9uX3R5cGVfZnJlbmNoX2xldmVsX3llYXIucG5nIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA1KQpgYGAKCmBgYHtyfQpwX2FjY19sZXZlbF95ZWFyIDwtIGdncGxvdChhY2N1cmFjeV9zdHJhdF9ieV93ZWVrW2NvdXJzZSA9PSAiRW5nbGlzaCIgJiBsZXZlbCAhPSAiT3RoZXIiICYgcXVlc3Rpb25fdHlwZSA9PSAiTXVsdGlwbGVcbmNob2ljZSIsXSwKICAgICAgICAgICAgYWVzKHggPSBhcy5QT1NJWGN0KGRveV9wb3NpeF9hbGlnbmVkX3dlZWspLCB5ID0gYWNjdXJhY3lfbWVhbiwgZ3JvdXAgPSBpbnRlcmFjdGlvbihzY2hvb2xfeWVhciwgcXVlc3Rpb25fdHlwZSksIGNvbG91ciA9IHNjaG9vbF95ZWFyLCBmaWxsID0gc2Nob29sX3llYXIpKSArCiAgZmFjZXRfZ3JpZChsZXZlbCB+IHllYXIpICsKICBnZW9tX3JlY3QoeG1pbiA9IGRhdGVfc2Nob29sc19jbG9zZWQsIHhtYXggPSBkYXRlX3NjaG9vbHNfb3BlbmVkLCB5bWluID0gMCwgeW1heCA9IDEuMDUsIGZpbGwgPSAiZ3JleTkyIiwgY29sb3VyID0gImdyZXk1MCIsIGx0eSA9IDIsIGFscGhhID0gLjkpICsKICBnZW9tX3JpYmJvbihhZXMoeW1pbiA9IGFjY3VyYWN5X21lYW4gLSBhY2N1cmFjeV9zZSwgeW1heCA9IGFjY3VyYWN5X21lYW4gKyBhY2N1cmFjeV9zZSwgY29sb3VyID0gTlVMTCksIGFscGhhID0gMC4yKSArCiAgZ2VvbV9saW5lKCkgKwogIHNjYWxlX3hfZGF0ZXRpbWUoZXhwYW5kID0gYygwLCAwKSwKICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGFzLlBPU0lYY3QoYygKICAgICAgICAgICAgICAgICAgICAgIjIwMTktMTAtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMTktMTItMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDItMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDQtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDYtMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgbGltaXRzID0gYXMuUE9TSVhjdChjKCIyMDE5LTA5LTAxIDAyOjAwOjAwIENFVCIsICIyMDIwLTA3LTAxIDAyOjAwOjAwIENFVCIpKSwKICAgICAgICAgICAgICAgICAgIGRhdGVfbGFiZWxzID0gIiViIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KGFjY3VyYWN5ID0gMSkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoLjQsIDEpKSArCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXNfZChlbmQgPSAuNSwgZGlyZWN0aW9uID0gLTEsIG5hLnRyYW5zbGF0ZSA9IEZBTFNFKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoZW5kID0gLjUsIGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gIkFjY3VyYWN5IiwKICAgICAgIGNvbG91ciA9ICJTY2hvb2wgeWVhciIsCiAgICAgICBmaWxsID0gIlNjaG9vbCB5ZWFyIiwKICAgICAgIGx0eSA9ICJRdWVzdGlvbiB0eXBlIikgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxKSwKICAgICAgICAgZmlsbCA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEpLAogICAgICAgICBsdHkgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAyKSkgKwogIHRoZW1lX3BhcGVyCgpwX2FjY19sZXZlbF95ZWFyCgpnZ3NhdmUoIi4uL291dHB1dC9hY2NfYnlfcXVlc3Rpb25fdHlwZV9lbmdsaXNoX2xldmVsX3llYXIucGRmIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA1KQpnZ3NhdmUoIi4uL291dHB1dC9hY2NfYnlfcXVlc3Rpb25fdHlwZV9lbmdsaXNoX2xldmVsX3llYXIuZXBzIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA1KQpnZ3NhdmUoIi4uL291dHB1dC9hY2NfYnlfcXVlc3Rpb25fdHlwZV9lbmdsaXNoX2xldmVsX3llYXIucG5nIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA1KQpgYGAKCiMjIyBCeSBsZXZlbAoKYGBge3J9CmFjY3VyYWN5X2xldmVsX2J5X3dlZWtfYW5kX3VzZXIgPC0gYWNjdXJhY3lfc3RyYXRbLCAuKGFjY3VyYWN5ID0gc3VtKGFjY3VyYWN5Km4pL3N1bShuKSksIGJ5ID0gLihjb3Vyc2UsIHNjaG9vbF95ZWFyLCBkb3lfcG9zaXhfYWxpZ25lZF93ZWVrLCBsZXZlbCwgdXNlciwgbWNxKV0KCmFjY3VyYWN5X2xldmVsX2J5X3dlZWsgPC0gYWNjdXJhY3lfbGV2ZWxfYnlfd2Vla19hbmRfdXNlclssIC4oYWNjdXJhY3lfbWVhbiA9IG1lYW4oYWNjdXJhY3ksIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFjY3VyYWN5X3NlID0gc2QoYWNjdXJhY3ksIG5hLnJtID0gVFJVRSkvc3FydCguTiksIG4gPSAuTiksIGJ5ID0gLihjb3Vyc2UsIHNjaG9vbF95ZWFyLCBkb3lfcG9zaXhfYWxpZ25lZF93ZWVrLCBsZXZlbCwgbWNxKV0KYGBgCgpBZGQgcXVlc3Rpb24gdHlwZSBsYWJlbHM6CmBgYHtyfQphY2N1cmFjeV9sZXZlbF9ieV93ZWVrWywgcXVlc3Rpb25fdHlwZSA6PSBpZmVsc2UobWNxID09IFRSVUUsICJNdWx0aXBsZVxuY2hvaWNlIiwgIk9wZW5cbmFuc3dlciIpXQpgYGAKCkhvdyBtYW55IHVzZXJzIGluIGVhY2ggZ3JvdXA/CmBgYHtyfQphY2N1cmFjeV9sZXZlbF9ieV93ZWVrX2FuZF91c2VyWywgLih1bmlxdWVfdXNlcnMgPSBsZW5ndGgodW5pcXVlKHVzZXIpKSksICBieSA9IC4oY291cnNlLCBsZXZlbCwgc2Nob29sX3llYXIsIG1jcSldCmBgYAoKClBsb3QgcmVzcG9uc2UgYWNjdXJhY3kgYnkgd2VlayAobWVhbiArLy0gMSBTRSkuCmBgYHtyfQpwX2FjY19sZXZlbCA8LSBnZ3Bsb3QoYWNjdXJhY3lfbGV2ZWxfYnlfd2Vla1soKGNvdXJzZSA9PSAiRW5nbGlzaCIgJiBxdWVzdGlvbl90eXBlID09ICJNdWx0aXBsZVxuY2hvaWNlIikgfCBjb3Vyc2UgPT0gIkZyZW5jaCIpICYgbGV2ZWwgIT0gIk90aGVyIixdLAogICAgICAgICAgICBhZXMoeCA9IGFzLlBPU0lYY3QoZG95X3Bvc2l4X2FsaWduZWRfd2VlayksIHkgPSBhY2N1cmFjeV9tZWFuLCBncm91cCA9IGludGVyYWN0aW9uKHNjaG9vbF95ZWFyLCBxdWVzdGlvbl90eXBlKSwgY29sb3VyID0gc2Nob29sX3llYXIsIGZpbGwgPSBzY2hvb2xfeWVhcikpICsKICBmYWNldF9ncmlkKGxldmVsIH4gY291cnNlKSArCiAgZ2VvbV9yZWN0KHhtaW4gPSBkYXRlX3NjaG9vbHNfY2xvc2VkLCB4bWF4ID0gZGF0ZV9zY2hvb2xzX29wZW5lZCwgeW1pbiA9IDAsIHltYXggPSAxLjA1LCBmaWxsID0gImdyZXk5MiIsIGNvbG91ciA9ICJncmV5NTAiLCBsdHkgPSAyLCBhbHBoYSA9IC45KSArCiAgZ2VvbV9yaWJib24oYWVzKHltaW4gPSBhY2N1cmFjeV9tZWFuIC0gYWNjdXJhY3lfc2UsIHltYXggPSBhY2N1cmFjeV9tZWFuICsgYWNjdXJhY3lfc2UsIGNvbG91ciA9IE5VTEwpLCBhbHBoYSA9IDAuMikgKwogIGdlb21fbGluZShhZXMobHR5ID0gcXVlc3Rpb25fdHlwZSkpICsKICBzY2FsZV94X2RhdGV0aW1lKGV4cGFuZCA9IGMoMCwgMCksCiAgICAgICAgICAgICAgICAgICBicmVha3MgPSBhcy5QT1NJWGN0KGMoCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTEwLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTEyLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTAyLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA0LTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA2LTAxIDAyOjAwOjAwIENFVCIpKSwKICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGFzLlBPU0lYY3QoYygiMjAxOS0wOS0wMSAwMjowMDowMCBDRVQiLCAiMjAyMC0wNy0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdChhY2N1cmFjeSA9IDEpKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKC42LCAxKSkgKwogIHNjYWxlX2NvbG91cl92aXJpZGlzX2QoZW5kID0gLjUsIGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKGVuZCA9IC41LCBkaXJlY3Rpb24gPSAtMSwgbmEudHJhbnNsYXRlID0gRkFMU0UpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJBY2N1cmFjeSIsCiAgICAgICBjb2xvdXIgPSAiU2Nob29sIHllYXIiLAogICAgICAgZmlsbCA9ICJTY2hvb2wgeWVhciIsCiAgICAgICBsdHkgPSAiUXVlc3Rpb24gdHlwZSIpICsKICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMSksCiAgICAgICAgIGZpbGwgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxKSwKICAgICAgICAgbHR5ID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMikpICsKICB0aGVtZV9wYXBlcgoKcF9hY2NfbGV2ZWwKCmdnc2F2ZSgiLi4vb3V0cHV0L2FjY19ieV9xdWVzdGlvbl90eXBlX2xldmVsLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gNSkKZ2dzYXZlKCIuLi9vdXRwdXQvYWNjX2J5X3F1ZXN0aW9uX3R5cGVfbGV2ZWwuZXBzIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA1KQpnZ3NhdmUoIi4uL291dHB1dC9hY2NfYnlfcXVlc3Rpb25fdHlwZV9sZXZlbC5wbmciLCB3aWR0aCA9IDksIGhlaWdodCA9IDUpCmBgYAoKCiMjIyBCeSB5ZWFyCgpgYGB7cn0KYWNjdXJhY3lfeWVhcl9ieV93ZWVrX2FuZF91c2VyIDwtIGFjY3VyYWN5X3N0cmF0WywgLihhY2N1cmFjeSA9IHN1bShhY2N1cmFjeSpuKS9zdW0obikpLCBieSA9IC4oY291cnNlLCBzY2hvb2xfeWVhciwgZG95X3Bvc2l4X2FsaWduZWRfd2VlaywgeWVhciwgdXNlciwgbWNxKV0KCmFjY3VyYWN5X3llYXJfYnlfd2VlayA8LSBhY2N1cmFjeV95ZWFyX2J5X3dlZWtfYW5kX3VzZXJbLCAuKGFjY3VyYWN5X21lYW4gPSBtZWFuKGFjY3VyYWN5LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhY2N1cmFjeV9zZSA9IHNkKGFjY3VyYWN5LCBuYS5ybSA9IFRSVUUpL3NxcnQoLk4pLCBuID0gLk4pLCBieSA9IC4oY291cnNlLCBzY2hvb2xfeWVhciwgZG95X3Bvc2l4X2FsaWduZWRfd2VlaywgeWVhciwgbWNxKV0KYGBgCgpBZGQgcXVlc3Rpb24gdHlwZSBsYWJlbHM6CmBgYHtyfQphY2N1cmFjeV95ZWFyX2J5X3dlZWtbLCBxdWVzdGlvbl90eXBlIDo9IGlmZWxzZShtY3EgPT0gVFJVRSwgIk11bHRpcGxlXG5jaG9pY2UiLCAiT3BlblxuYW5zd2VyIildCmBgYAoKSG93IG1hbnkgdXNlcnMgaW4gZWFjaCBncm91cD8KYGBge3J9CmFjY3VyYWN5X3llYXJfYnlfd2Vla19hbmRfdXNlclssIC4odW5pcXVlX3VzZXJzID0gbGVuZ3RoKHVuaXF1ZSh1c2VyKSkpLCAgYnkgPSAuKGNvdXJzZSwgeWVhciwgc2Nob29sX3llYXIsIG1jcSldCmBgYAoKClBsb3QgcmVzcG9uc2UgYWNjdXJhY3kgYnkgd2VlayAobWVhbiArLy0gMSBTRSkuCmBgYHtyfQpwX2FjY195ZWFyIDwtIGdncGxvdChhY2N1cmFjeV95ZWFyX2J5X3dlZWtbKChjb3Vyc2UgPT0gIkVuZ2xpc2giICYgcXVlc3Rpb25fdHlwZSA9PSAiTXVsdGlwbGVcbmNob2ljZSIpIHwgY291cnNlID09ICJGcmVuY2giKSAmIHllYXIgIT0gIk90aGVyIixdLAogICAgICAgICAgICBhZXMoeCA9IGFzLlBPU0lYY3QoZG95X3Bvc2l4X2FsaWduZWRfd2VlayksIHkgPSBhY2N1cmFjeV9tZWFuLCBncm91cCA9IGludGVyYWN0aW9uKHNjaG9vbF95ZWFyLCBxdWVzdGlvbl90eXBlKSwgY29sb3VyID0gc2Nob29sX3llYXIsIGZpbGwgPSBzY2hvb2xfeWVhcikpICsKICBmYWNldF9ncmlkKHllYXIgfiBjb3Vyc2UpICsKICBnZW9tX3JlY3QoeG1pbiA9IGRhdGVfc2Nob29sc19jbG9zZWQsIHhtYXggPSBkYXRlX3NjaG9vbHNfb3BlbmVkLCB5bWluID0gMCwgeW1heCA9IDEuMDUsIGZpbGwgPSAiZ3JleTkyIiwgY29sb3VyID0gImdyZXk1MCIsIGx0eSA9IDIsIGFscGhhID0gLjkpICsKICBnZW9tX3JpYmJvbihhZXMoeW1pbiA9IGFjY3VyYWN5X21lYW4gLSBhY2N1cmFjeV9zZSwgeW1heCA9IGFjY3VyYWN5X21lYW4gKyBhY2N1cmFjeV9zZSwgY29sb3VyID0gTlVMTCksIGFscGhhID0gMC4yKSArCiAgZ2VvbV9saW5lKGFlcyhsdHkgPSBxdWVzdGlvbl90eXBlKSkgKwogIHNjYWxlX3hfZGF0ZXRpbWUoZXhwYW5kID0gYygwLCAwKSwKICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGFzLlBPU0lYY3QoYygKICAgICAgICAgICAgICAgICAgICAgIjIwMTktMTAtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMTktMTItMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDItMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDQtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDYtMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgbGltaXRzID0gYXMuUE9TSVhjdChjKCIyMDE5LTA5LTAxIDAyOjAwOjAwIENFVCIsICIyMDIwLTA3LTAxIDAyOjAwOjAwIENFVCIpKSwKICAgICAgICAgICAgICAgICAgIGRhdGVfbGFiZWxzID0gIiViIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KGFjY3VyYWN5ID0gMSkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoLjYsIDEpKSArCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXNfZChlbmQgPSAuNSwgZGlyZWN0aW9uID0gLTEsIG5hLnRyYW5zbGF0ZSA9IEZBTFNFKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoZW5kID0gLjUsIGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gIkFjY3VyYWN5IiwKICAgICAgIGNvbG91ciA9ICJTY2hvb2wgeWVhciIsCiAgICAgICBmaWxsID0gIlNjaG9vbCB5ZWFyIiwKICAgICAgIGx0eSA9ICJRdWVzdGlvbiB0eXBlIikgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxKSwKICAgICAgICAgZmlsbCA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEpLAogICAgICAgICBsdHkgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAyKSkgKwogIHRoZW1lX3BhcGVyCgpwX2FjY195ZWFyCgpnZ3NhdmUoIi4uL291dHB1dC9hY2NfYnlfcXVlc3Rpb25fdHlwZV95ZWFyLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gNSkKZ2dzYXZlKCIuLi9vdXRwdXQvYWNjX2J5X3F1ZXN0aW9uX3R5cGVfeWVhci5lcHMiLCB3aWR0aCA9IDksIGhlaWdodCA9IDUpCmdnc2F2ZSgiLi4vb3V0cHV0L2FjY19ieV9xdWVzdGlvbl90eXBlX3llYXIucG5nIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA1KQpgYGAKCgoKIyMjIFJlZ3Jlc3Npb24gbW9kZWwKCkZpdCBhIG1peGVkIGVmZmVjdHMgbW9kZWwgdG8gdGhlIGRhaWx5IGFjY3VyYWN5IGRhdGE6CmBgYHtyfQphY2N1cmFjeVssIHBlcmlvZCA6PSBkcGx5cjo6Y2FzZV93aGVuKAogIGRveV9wb3NpeF9hbGlnbmVkID49IGRhdGVfc2Nob29sc19vcGVuZWQgfiAicG9zdC1sb2NrZG93biIsCiAgZG95X3Bvc2l4X2FsaWduZWQgPj0gZGF0ZV9zY2hvb2xzX2Nsb3NlZCAmIGRveV9wb3NpeF9hbGlnbmVkIDwgZGF0ZV9zY2hvb2xzX29wZW5lZCB+ICJkdXJpbmctbG9ja2Rvd24iLAogIGRveV9wb3NpeF9hbGlnbmVkIDwgZGF0ZV9zY2hvb2xzX29wZW5lZCB+ICJwcmUtbG9ja2Rvd24iCildCgojIFJlb3JkZXIgZmFjdG9yIGxldmVscyBzbyB0aGF0IGludGVyY2VwdCBpcyBwcmUtbG9ja2Rvd24gb3BlbiBhbnN3ZXIgaW4gMTkvMjAKYWNjdXJhY3lbLCBwZXJpb2QgOj0gZmFjdG9yKHBlcmlvZCwgbGV2ZWxzID0gYygicHJlLWxvY2tkb3duIiwgImR1cmluZy1sb2NrZG93biIsICJwb3N0LWxvY2tkb3duIikpXQphY2N1cmFjeVssIHNjaG9vbF95ZWFyIDo9IGZhY3RvcihzY2hvb2xfeWVhciwgbGV2ZWxzID0gYygiMTkvMjAiLCAiMTgvMTkiKSldCmFjY3VyYWN5WywgbWNxIDo9IGZhY3RvcihtY3EsIGxldmVscyA9IGMoRkFMU0UsIFRSVUUpKV0KYGBgCgpTaW5jZSB3ZSBrbm93IHRoZSBudW1iZXIgb2YgdHJpYWxzIHBlciBkYXkgYW5kIHRoZSBwcm9wb3J0aW9uIGNvcnJlY3QgKGFjY3VyYWN5KSwgd2UgY2FuIHVzZSBhIGJpbm9taWFsIEdMTU06CmBgYHtyfQppZighZmlsZS5leGlzdHMoIi4uL291dHB1dC9tX2FjY19maXQucmRzIikpIHsKICBtX2FjYyA8LSBnbG1lcihhY2N1cmFjeSB+IHBlcmlvZCpzY2hvb2xfeWVhciptY3EgKyAoMSB8IHVzZXIpICsgKDEgfCBjb3Vyc2UpLAogICAgICAgICAgICAgICAgIGRhdGEgPSBhY2N1cmFjeVsoY291cnNlID09ICJFbmdsaXNoIiAmIG1jcSA9PSBUUlVFKSB8IGNvdXJzZSA9PSAiRnJlbmNoIixdLAogICAgICAgICAgICAgICAgIGZhbWlseSA9ICJiaW5vbWlhbCIsIAogICAgICAgICAgICAgICAgIHdlaWdodHMgPSBuLAogICAgICAgICAgICAgICAgIG5BR1EgPSAwLAogICAgICAgICAgICAgICAgIGNvbnRyb2wgPSBnbG1lckNvbnRyb2wob3B0aW1pemVyID0gImJvYnlxYSIsIG9wdEN0cmwgPSBsaXN0KG1heGZ1biA9IDFlNikpKQogIHNhdmVSRFMobV9hY2MsICIuLi9vdXRwdXQvbV9hY2NfZml0LnJkcyIpCn0gZWxzZSB7CiAgbV9hY2MgPC0gcmVhZFJEUygiLi4vb3V0cHV0L21fYWNjX2ZpdC5yZHMiKQp9CgptX2FjY19zdW1tYXJ5IDwtIHN1bW1hcnkobV9hY2MpCm1fYWNjX3N1bW1hcnkKYGBgCgpTYXZlIGNvZWZmaWNpZW50cyBhcyBhIHRhYmxlIGZvciBpbiB0aGUgcGFwZXI6CmBgYHtyfQptX2FjY19jb2VmIDwtIGFzLmRhdGEuZnJhbWUobV9hY2Nfc3VtbWFyeSRjb2VmZmljaWVudHMpCnNldERUKG1fYWNjX2NvZWYsIGtlZXAucm93bmFtZXMgPSBUUlVFKQptX2FjY19jb2VmJHJuIDwtIGMoIkludGVyY2VwdCBcXHNtYWxseyhQZXJpb2Q6IHByZS1sb2NrZG93biwgU2Nob29sIHllYXI6IDE5LzIwLCBRdWVzdGlvbiB0eXBlOiBvcGVuIGFuc3dlcil9IiwKICAgICAgICAgICAgICAgICAgICJQZXJpb2Q6IGxvY2tkb3duIiwKICAgICAgICAgICAgICAgICAgICJQZXJpb2Q6IHBvc3QtbG9ja2Rvd24iLAogICAgICAgICAgICAgICAgICAgIlNjaG9vbCB5ZWFyOiAxOC8xOSIsCiAgICAgICAgICAgICAgICAgICAiUXVlc3Rpb24gdHlwZTogbXVsdGlwbGUgY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICJQZXJpb2Q6IGxvY2tkb3duICRcXHRpbWVzJCBTY2hvb2wgeWVhcjogMTgvMTkiLAogICAgICAgICAgICAgICAgICAgIlBlcmlvZDogcG9zdC1sb2NrZG93biAkXFx0aW1lcyQgU2Nob29sIHllYXI6IDE4LzE5IiwKICAgICAgICAgICAgICAgICAgICJQZXJpb2Q6IGxvY2tkb3duICRcXHRpbWVzJCBRdWVzdGlvbiB0eXBlOiBtdWx0aXBsZSBjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgIlBlcmlvZDogcG9zdC1sb2NrZG93biAkXFx0aW1lcyQgUXVlc3Rpb24gdHlwZTogbXVsdGlwbGUgY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICJTY2hvb2wgeWVhcjogMTgvMTkgJFxcdGltZXMkIFF1ZXN0aW9uIHR5cGU6IG11bHRpcGxlIGNob2ljZSIsCiAgICAgICAgICAgICAgICAgICAiUGVyaW9kOiBsb2NrZG93biAkXFx0aW1lcyQgU2Nob29sIHllYXI6IDE4LzE5ICRcXHRpbWVzJCBRdWVzdGlvbiB0eXBlOiBtdWx0aXBsZSBjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgIlBlcmlvZDogcG9zdC1sb2NrZG93biAkXFx0aW1lcyQgU2Nob29sIHllYXI6IDE4LzE5ICRcXHRpbWVzJCBRdWVzdGlvbiB0eXBlOiBtdWx0aXBsZSBjaG9pY2UiKQoKIyBGb3JtYXQgcC12YWx1ZXMKbV9hY2NfY29lZiRgUHIoPnx6fClgIDwtIGZvcm1hdC5wdmFsKG1fYWNjX2NvZWYkYFByKD58enwpYCwgZXBzID0gLjAwMSwgZGlnaXRzID0gMywgZmxhZyA9ICIwIikKbV9hY2NfY29lZiRgUHIoPnx6fClgIDwtIHN1YignXig8KT8wWy5dJywgJ1xcMS4nLCBtX2FjY19jb2VmJGBQcig+fHp8KWApICMgUmVtb3ZlIGxlYWRpbmcgemVybwoKY2F0KGtuaXRyOjprYWJsZShtX2FjY19jb2VmLAogICAgICAgICAgICAgICAgIGFsaWduID0gYygibCIsInIiLCAiciIsICJyIiwgInIiKSwKICAgICAgICAgICAgICAgICBkaWdpdHMgPSBjKE5BLCAzLCAzLCAyLCBOQSksCiAgICAgICAgICAgICAgICAgY29sLm5hbWVzID0gYygiRWZmZWN0IiwgIiRiJCIsICJTRSIsICIkeiQiLCAiJHAkIiksCiAgICAgICAgICAgICAgICAgZm9ybWF0ID0gImxhdGV4IiwKICAgICAgICAgICAgICAgICBib29rdGFicyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgZXNjYXBlID0gRkFMU0UpLAogICAgZmlsZSA9ICIuLi9vdXRwdXQvbV9hY2NfdGFibGUudGV4IikKYGBgCgpWaXN1YWxpc2UgdGhlIG1vZGVsIGZpdDoKYGBge3J9CmFjY19maXQgPC0gZXhwYW5kLmdyaWQocGVyaW9kID0gYygicHJlLWxvY2tkb3duIiwgImR1cmluZy1sb2NrZG93biIsICJwb3N0LWxvY2tkb3duIiksIHNjaG9vbF95ZWFyID0gYygiMTgvMTkiLCAiMTkvMjAiKSwgbWNxID0gYyhUUlVFLCBGQUxTRSkpCmFjY19maXQgPC0gY2JpbmQoYWNjX2ZpdCwgYWNjdXJhY3kgPSBwcmVkaWN0KG1fYWNjLCB0eXBlID0gInJlc3BvbnNlIiwgcmUuZm9ybSA9IE5BLCBuZXdkYXRhID0gYWNjX2ZpdCkpCmFjY19maXQKCmdncGxvdChhY2NfZml0LCBhZXMoeCA9IHBlcmlvZCwgeSA9IGFjY3VyYWN5LCBjb2xvdXIgPSBzY2hvb2xfeWVhciwgbHR5ID0gbWNxLCBncm91cCA9IGludGVyYWN0aW9uKG1jcSwgc2Nob29sX3llYXIpKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKC43LCAxKSwgbGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdCgpKSArCiAgdGhlbWVfcGFwZXIKYGBgCgpFbXBpcmljYWwgbWVhbnM6CmBgYHtyfQphY2N1cmFjeV9tZWFuIDwtIGFjY3VyYWN5Wyhjb3Vyc2UgPT0gIkVuZ2xpc2giICYgbWNxID09IFRSVUUpIHwgY291cnNlID09ICJGcmVuY2giLCAuKGFjY3VyYWN5ID0gc3VtKGFjY3VyYWN5ICogbikvc3VtKG4pKSwgYnkgPSAuKHBlcmlvZCwgc2Nob29sX3llYXIsIG1jcSwgdXNlciwgY291cnNlKV1bLCAuKGFjY3VyYWN5ID0gbWVhbihhY2N1cmFjeSksIGFjY3VyYWN5X3NkID0gc2QoYWNjdXJhY3kpKSwgYnkgPSAuKHBlcmlvZCwgc2Nob29sX3llYXIsIG1jcSldCmFjY3VyYWN5X21lYW4KYGBgCgpgYGB7cn0KZ2dwbG90KGFjY3VyYWN5X21lYW4sIGFlcyh4ID0gcGVyaW9kLCB5ID0gYWNjdXJhY3ksIGNvbG91ciA9IHNjaG9vbF95ZWFyLCBsdHkgPSBtY3EsIGdyb3VwID0gaW50ZXJhY3Rpb24obWNxLCBzY2hvb2xfeWVhcikpKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoLjcsIDEpLCBsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KCkpICsKICB0aGVtZV9wYXBlcgpgYGAKCgoKCiMjIFJlc3BvbnNlIHRpbWUKCmBgYHtyfQpkYiA8LSBkYl9jb25uZWN0KCkKcnQgPC0gZGJHZXRRdWVyeShkYiwgCiAgICAgICAgICAgICAgICAgIlNFTEVDVCByLm1ldGhvZCBBUyAnbWV0aG9kJywKICAgICAgICAgICAgICAgICAgci5ib29rX2luZm9faWQgQVMgJ2Jvb2tfaW5mb19pZCcsCiAgICAgICAgICAgICAgICAgIERBVEUoci5kYXRlICsgMzYwMCwgJ3VuaXhlcG9jaCcpIEFTICdkb3knLAogICAgICAgICAgICAgICAgICByLnVzZXJfaWQgQVMgJ3VzZXInLAogICAgICAgICAgICAgICAgICByLmNob2ljZXMgPiAxIEFTICdtY3EnLAogICAgICAgICAgICAgICAgICByLnJ0IEFTICdydCcKICAgICAgICAgICAgICAgICAgRlJPTSAncmVzcG9uc2VzX25vZHVwbGljYXRlcycgcgogICAgICAgICAgICAgICAgICBXSEVSRSByLnN0dWR5ID09IDAKICAgICAgICAgICAgICAgICAgQU5EIHIuY29ycmVjdCA9PSAxCiAgICAgICAgICAgICAgICAgIgopCnNldERUKHJ0KQpkYl9kaXNjb25uZWN0KGRiKQpgYGAKCmBgYHtyfQpkb3lzIDwtIHJ0WywgLihkb3kgPSB1bmlxdWUoZG95KSldWywgZG95X3Bvc2l4IDo9IGFzLlBPU0lYY3QoZG95KV1bXQpkb3lzWywgZG95X3Bvc2l4X3dlZWsgOj0gY3V0LlBPU0lYdChhcy5QT1NJWGN0KGRveSksICJ3ZWVrIildCmRveXNbLCBzY2hvb2xfeWVhciA6PSBpZmVsc2UoZG95X3Bvc2l4IDwgIjIwMTktMDgtMDEiLCAiMTgvMTkiLCAiMTkvMjAiKV0KZG95c1tzY2hvb2xfeWVhciA9PSAiMTgvMTkiLCBkb3lfcG9zaXhfYWxpZ25lZCA6PSBhcy5QT1NJWGN0KGRveV9wb3NpeCArIDM2NSoyNCo2MCo2MCwgb3JpZ2luID0gIjE5NzAtMDEtMDEiKV0KZG95c1tzY2hvb2xfeWVhciA9PSAiMTkvMjAiLCBkb3lfcG9zaXhfYWxpZ25lZCA6PSBkb3lfcG9zaXhdCmRveXNbLCBkb3lfcG9zaXhfYWxpZ25lZF93ZWVrIDo9IGN1dC5QT1NJWHQoZG95X3Bvc2l4X2FsaWduZWQsICJ3ZWVrIildCmRveXNbLCBwZXJpb2QgOj0gZHBseXI6OmNhc2Vfd2hlbigKICBkb3lfcG9zaXhfYWxpZ25lZCA+PSBkYXRlX3NjaG9vbHNfb3BlbmVkIH4gInBvc3QtbG9ja2Rvd24iLAogIGRveV9wb3NpeF9hbGlnbmVkID49IGRhdGVfc2Nob29sc19jbG9zZWQgJiBkb3lfcG9zaXhfYWxpZ25lZCA8IGRhdGVfc2Nob29sc19vcGVuZWQgfiAiZHVyaW5nLWxvY2tkb3duIiwKICBkb3lfcG9zaXhfYWxpZ25lZCA8IGRhdGVfc2Nob29sc19vcGVuZWQgfiAicHJlLWxvY2tkb3duIgopXQoKIyBSZW9yZGVyIGZhY3RvciBsZXZlbHMgc28gdGhhdCBpbnRlcmNlcHQgaXMgcHJlLWxvY2tkb3duIGluIDE5LzIwCmRveXNbLCBwZXJpb2QgOj0gZmFjdG9yKHBlcmlvZCwgbGV2ZWxzID0gYygicHJlLWxvY2tkb3duIiwgImR1cmluZy1sb2NrZG93biIsICJwb3N0LWxvY2tkb3duIikpXQpkb3lzWywgc2Nob29sX3llYXIgOj0gZmFjdG9yKHNjaG9vbF95ZWFyLCBsZXZlbHMgPSBjKCIxOS8yMCIsICIxOC8xOSIpKV0KYGBgCgpgYGB7cn0KcnQgPC0gcnRbZG95cywgb24gPSAiZG95Il0KYGBgCgpgYGB7cn0KcnRbLCBtY3EgOj0gYXMuZmFjdG9yKGFzLmxvZ2ljYWwobWNxKSldCmBgYAoKYGBge3J9CnJ0WywgY291cnNlIDo9IGlmZWxzZShtZXRob2QgPT0gIkdyYW5kZXMgTGlnbmVzIiwgIkZyZW5jaCIsIGlmZWxzZShtZXRob2QgPT0gIlN0ZXBwaW5nIFN0b25lcyIsICJFbmdsaXNoIiwgIkdlcm1hbiIpKV0KYGBgCgpUaHJvdyBvdXQgdHJpYWxzIHdpdGggbmVnYXRpdmUgUlRzICh0aW1pbmcgZXJyb3JzKQpgYGB7cn0KcnQgPC0gcnRbcnQgPiAwXQpgYGAKCgoKIyMjIFdob2xlIHBvcHVsYXRpb24KCmBgYHtyfQpydF9tZWQgPC0gcnRbLCAuKHJ0X21lZGlhbiA9IG1lZGlhbihydCkpLCBieSA9IC4oc2Nob29sX3llYXIsIG1jcSwgdXNlciwgY291cnNlLCBkb3lfcG9zaXhfd2VlayldCgpydF9ieV93ZWVrIDwtIHJ0X21lZFssIC4ocnQgPSBtZWFuKHJ0X21lZGlhbiksIHJ0X3NlID0gc2QocnRfbWVkaWFuKS9zcXJ0KC5OKSksIGJ5ID0gLihzY2hvb2xfeWVhciwgbWNxLCBjb3Vyc2UsIGRveV9wb3NpeF93ZWVrKV0KYGBgCgpPdmVybGFwIHRoZSB0d28gc2Nob29sIHllYXJzOgpgYGB7cn0KcnRfYnlfd2Vla1tzY2hvb2xfeWVhciA9PSAiMTgvMTkiLCBkb3lfcG9zaXhfd2Vla19hbGlnbmVkIDo9IGFzLlBPU0lYY3QoYXMuUE9TSVhjdChkb3lfcG9zaXhfd2VlaykgKyAzNjUqMjQqNjAqNjAsIG9yaWdpbiA9ICIxOTcwLTAxLTAxIildCnJ0X2J5X3dlZWtbc2Nob29sX3llYXIgPT0gIjE5LzIwIiwgZG95X3Bvc2l4X3dlZWtfYWxpZ25lZCA6PSBhcy5QT1NJWGN0KGRveV9wb3NpeF93ZWVrKV0KYGBgCgpBZGQgcXVlc3Rpb24gdHlwZSBsYWJlbHM6CmBgYHtyfQpydF9ieV93ZWVrWywgcXVlc3Rpb25fdHlwZSA6PSBpZmVsc2UobWNxID09IFRSVUUsICJNdWx0aXBsZVxuY2hvaWNlIiwgIk9wZW5cbmFuc3dlciIpXQpgYGAKCmBgYHtyfQpydF9ieV93ZWVrWywgc2Nob29sX3llYXIgOj0gZmFjdG9yKHNjaG9vbF95ZWFyLCBsZXZlbHMgPSBjKCIxOC8xOSIsICIxOS8yMCIpKV0KYGBgCgoKUGxvdCByZXNwb25zZSB0aW1lIGJ5IHdlZWsgKG1lYW4gKy8tIDEgU0UpLgpgYGB7cn0KcF9ydCA8LSBnZ3Bsb3QocnRfYnlfd2Vla1soY291cnNlID09ICJFbmdsaXNoIiAmIG1jcSA9PSBUUlVFKSB8IGNvdXJzZSA9PSAiRnJlbmNoIixdLAogICAgICAgICAgICBhZXMoeCA9IGRveV9wb3NpeF93ZWVrX2FsaWduZWQsIHkgPSBydC8xZTMsIGdyb3VwID0gaW50ZXJhY3Rpb24oc2Nob29sX3llYXIsIHF1ZXN0aW9uX3R5cGUpLCBjb2xvdXIgPSBzY2hvb2xfeWVhciwgZmlsbCA9IHNjaG9vbF95ZWFyKSkgKwogIGZhY2V0X2dyaWQoLiB+IGNvdXJzZSkgKwogIGdlb21fcmVjdCh4bWluID0gZGF0ZV9zY2hvb2xzX2Nsb3NlZCwgeG1heCA9IGRhdGVfc2Nob29sc19vcGVuZWQsIHltaW4gPSAwLCB5bWF4ID0gMTAwMCwgZmlsbCA9ICJncmV5OTIiLCBjb2xvdXIgPSAiZ3JleTUwIiwgbHR5ID0gMiwgYWxwaGEgPSAuOSkgKwogIGdlb21fcmliYm9uKGFlcyh5bWluID0gcnQvMWUzIC0gcnRfc2UvMWUzLCB5bWF4ID0gcnQvMWUzICsgcnRfc2UvMWUzLCBjb2xvdXIgPSBOVUxMKSwgYWxwaGEgPSAwLjIpICsKICBnZW9tX2xpbmUoYWVzKGx0eSA9IHF1ZXN0aW9uX3R5cGUpKSArCiAgc2NhbGVfeF9kYXRldGltZShleHBhbmQgPSBjKDAsIDApLAogICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYXMuUE9TSVhjdChjKAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0xMC0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0xMi0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wMi0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNC0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNi0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBhcy5QT1NJWGN0KGMoIjIwMTktMDktMDEgMDI6MDA6MDAgQ0VUIiwgIjIwMjAtMDctMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgZGF0ZV9sYWJlbHMgPSAiJWIiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6dW5pdF9mb3JtYXQodW5pdCA9ICJzIiwgYWNjdXJhY3kgPSAuMSkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMS43LCAzLjcpKSArCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXNfZChlbmQgPSAuNSwgZGlyZWN0aW9uID0gLTEsIG5hLnRyYW5zbGF0ZSA9IEZBTFNFKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoZW5kID0gLjUsIGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gIlJlc3BvbnNlIHRpbWUiLAogICAgICAgY29sb3VyID0gIlNjaG9vbCB5ZWFyIiwKICAgICAgIGZpbGwgPSAiU2Nob29sIHllYXIiLAogICAgICAgbHR5ID0gIlF1ZXN0aW9uIHR5cGUiKSArCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEpLAogICAgICAgICBmaWxsID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMSksCiAgICAgICAgIGx0eSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDIpKSArCiAgdGhlbWVfcGFwZXIKCnBfcnQKCmdnc2F2ZSgiLi4vb3V0cHV0L3J0X2J5X3F1ZXN0aW9uX3R5cGUucGRmIiwgd2lkdGggPSA5LCBoZWlnaHQgPSAzKQpnZ3NhdmUoIi4uL291dHB1dC9ydF9ieV9xdWVzdGlvbl90eXBlLmVwcyIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gMykKZ2dzYXZlKCIuLi9vdXRwdXQvcnRfYnlfcXVlc3Rpb25fdHlwZS5wbmciLCB3aWR0aCA9IDksIGhlaWdodCA9IDMpCmBgYAoKIyMjIEJ5IGxldmVsIGFuZCB5ZWFyCgpBZGQgYm9vayBpbmZvOgpgYGB7cn0KcnRbYm9va19pbmZvW2Jvb2tfdHlwZSA9PSAiSG9vZmRib2VrIixdLCBvbiAgPSAiYm9va19pbmZvX2lkIiwgYygibWV0aG9kX2dyb3VwIiwgImJvb2tfdGl0bGUiKSA6PSAuKGkubWV0aG9kX2dyb3VwLCBpLmJvb2tfdGl0bGUpXQpgYGAKClNpbXBsaWZ5IGxldmVsIG5hbWVzOgpgYGB7cn0KIyBLZWVwIGFsbCBkaXN0aW5jdGlvbnMKcnRbLCBib29rX3RpdGxlX3NpbXBsZSA6PSBzdHJpbmdyOjpzdHJfc3ViKGJvb2tfdGl0bGUsIDMsIC0xMCldCnJ0WywgYm9va190aXRsZV9zaW1wbGUgOj0gZmFjdG9yKGJvb2tfdGl0bGVfc2ltcGxlLCBsZXZlbHMgPSBjKCJ2bWJvIGIvbHdvbyIsICJ2bWJvIGIiLCAidm1ibyBiayIsICJ2bWJvIGsiLCAidm1ibyBrZ3QiLCAidm1iby1ndCIsICJ2bWJvIGd0IiwgInZtYm8tZ3QvaGF2byIsICJ2bWJvICh0KWh2IiwgImhhdm8iLCAiaGF2byB2d28iLCAidndvIikpXQoKIyBTaW1wbGlmeSB0byB0aHJlZSBsZXZlbHMKcnRbLCBsZXZlbCA6PSBkcGx5cjo6Y2FzZV93aGVuKAogIGdyZXBsKCAiaHYiLCBib29rX3RpdGxlKSB+ICJHZW5lcmFsIHNlY29uZGFyeVxuKGhhdm8pIiwKICBncmVwbCgidm1ibyIsIGJvb2tfdGl0bGUpIH4gIlByZS12b2NhdGlvbmFsXG4odm1ibykiLAogIGdyZXBsKCJoYXZvIiwgYm9va190aXRsZSkgfiAiR2VuZXJhbCBzZWNvbmRhcnlcbihoYXZvKSIsCiAgZ3JlcGwoInZ3byIsIGJvb2tfdGl0bGUpIH4gIlByZS11bml2ZXJzaXR5XG4odndvKSIsCiAgVFJVRSB+ICJPdGhlciIpXQpydFssIGxldmVsIDo9IGZhY3RvcihsZXZlbCwgbGV2ZWxzID0gYygiT3RoZXIiLCAiUHJlLXZvY2F0aW9uYWxcbih2bWJvKSIsICJHZW5lcmFsIHNlY29uZGFyeVxuKGhhdm8pIiwgIlByZS11bml2ZXJzaXR5XG4odndvKSIpKV0KYGBgCgpTaW1wbGlmeSB5ZWFyIG5hbWVzOgpgYGB7cn0KcnRbLCB5ZWFyIDo9IGRwbHlyOjpjYXNlX3doZW4oCiAgbWV0aG9kX2dyb3VwID09ICJMZWVyamFhciAxICg1ZSBFZC4pIiB+ICJZZWFyIDEiLAogIG1ldGhvZF9ncm91cCA9PSAiTGVlcmphYXIgMiAoNWUgRWQuKSIgfiAiWWVhciAyIiwKICBtZXRob2RfZ3JvdXAgPT0gIkxlZXJqYWFyIDMgKDVlIEVkLikiIH4gIlllYXIgMyIsCiAgbWV0aG9kX2dyb3VwID09ICJMZWVyamFhciAzLzQgKDVlIEVkLikiIH4gIlllYXIgMy80IiwKICBtZXRob2RfZ3JvdXAgPT0gIkxlZXJqYWFyIDQgKDVlIEVkLikiIH4gIlllYXIgNCIsCiAgbWV0aG9kX2dyb3VwID09ICJUd2VlZGUgRmFzZSAoNmUgRWQuKSIgfiAiVHdlZWRlIEZhc2UiLAogIFRSVUUgfiAiT3RoZXIiKV0KYGBgCgoKYGBge3J9CnJ0X3N0cmF0X21lZCA8LSBydFssIC4ocnRfbWVkaWFuID0gbWVkaWFuKHJ0KSksIGJ5ID0gLihzY2hvb2xfeWVhciwgbWNxLCB1c2VyLCBjb3Vyc2UsIGxldmVsLCB5ZWFyLCBkb3lfcG9zaXhfd2VlayldCgpydF9zdHJhdF9ieV93ZWVrIDwtIHJ0X3N0cmF0X21lZFssIC4ocnQgPSBtZWFuKHJ0X21lZGlhbiksIHJ0X3NlID0gc2QocnRfbWVkaWFuKS9zcXJ0KC5OKSksIGJ5ID0gLihzY2hvb2xfeWVhciwgbWNxLCBjb3Vyc2UsIGxldmVsLCB5ZWFyLCBkb3lfcG9zaXhfd2VlayldCmBgYAoKT3ZlcmxhcCB0aGUgdHdvIHNjaG9vbCB5ZWFyczoKYGBge3J9CnJ0X3N0cmF0X2J5X3dlZWtbc2Nob29sX3llYXIgPT0gIjE4LzE5IiwgZG95X3Bvc2l4X3dlZWtfYWxpZ25lZCA6PSBhcy5QT1NJWGN0KGFzLlBPU0lYY3QoZG95X3Bvc2l4X3dlZWspICsgMzY1KjI0KjYwKjYwLCBvcmlnaW4gPSAiMTk3MC0wMS0wMSIpXQpydF9zdHJhdF9ieV93ZWVrW3NjaG9vbF95ZWFyID09ICIxOS8yMCIsIGRveV9wb3NpeF93ZWVrX2FsaWduZWQgOj0gYXMuUE9TSVhjdChkb3lfcG9zaXhfd2VlayldCmBgYAoKQWRkIHF1ZXN0aW9uIHR5cGUgbGFiZWxzOgpgYGB7cn0KcnRfc3RyYXRfYnlfd2Vla1ssIHF1ZXN0aW9uX3R5cGUgOj0gaWZlbHNlKG1jcSA9PSBUUlVFLCAiTXVsdGlwbGVcbmNob2ljZSIsICJPcGVuXG5hbnN3ZXIiKV0KYGBgCgpgYGB7cn0KcnRfc3RyYXRfYnlfd2Vla1ssIHNjaG9vbF95ZWFyIDo9IGZhY3RvcihzY2hvb2xfeWVhciwgbGV2ZWxzID0gYygiMTgvMTkiLCAiMTkvMjAiKSldCmBgYAoKClBsb3QgcmVzcG9uc2UgdGltZSBieSB3ZWVrIChtZWFuICsvLSAxIFNFKS4KYGBge3J9CnBfcnRfbGV2ZWxfeWVhciA8LSBnZ3Bsb3QocnRfc3RyYXRfYnlfd2Vla1tjb3Vyc2UgPT0gIkZyZW5jaCIsXSwKICAgICAgICAgICAgYWVzKHggPSBkb3lfcG9zaXhfd2Vla19hbGlnbmVkLCB5ID0gcnQvMWUzLCBncm91cCA9IGludGVyYWN0aW9uKHNjaG9vbF95ZWFyLCBxdWVzdGlvbl90eXBlKSwgY29sb3VyID0gc2Nob29sX3llYXIsIGZpbGwgPSBzY2hvb2xfeWVhcikpICsKICBmYWNldF9ncmlkKGxldmVsIH4geWVhcikgKwogIGdlb21fcmVjdCh4bWluID0gZGF0ZV9zY2hvb2xzX2Nsb3NlZCwgeG1heCA9IGRhdGVfc2Nob29sc19vcGVuZWQsIHltaW4gPSAwLCB5bWF4ID0gMTAwMCwgZmlsbCA9ICJncmV5OTIiLCBjb2xvdXIgPSAiZ3JleTUwIiwgbHR5ID0gMiwgYWxwaGEgPSAuOSkgKwogIGdlb21fcmliYm9uKGFlcyh5bWluID0gcnQvMWUzIC0gcnRfc2UvMWUzLCB5bWF4ID0gcnQvMWUzICsgcnRfc2UvMWUzLCBjb2xvdXIgPSBOVUxMKSwgYWxwaGEgPSAwLjIpICsKICBnZW9tX2xpbmUoYWVzKGx0eSA9IHF1ZXN0aW9uX3R5cGUpKSArCiAgc2NhbGVfeF9kYXRldGltZShleHBhbmQgPSBjKDAsIDApLAogICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYXMuUE9TSVhjdChjKAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0xMC0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0xMi0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wMi0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNC0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNi0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBhcy5QT1NJWGN0KGMoIjIwMTktMDktMDEgMDI6MDA6MDAgQ0VUIiwgIjIwMjAtMDctMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgZGF0ZV9sYWJlbHMgPSAiJWIiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6dW5pdF9mb3JtYXQodW5pdCA9ICJzIiwgYWNjdXJhY3kgPSAuMSkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMSwgNCkpICsKICBzY2FsZV9jb2xvdXJfdmlyaWRpc19kKGVuZCA9IC41LCBkaXJlY3Rpb24gPSAtMSwgbmEudHJhbnNsYXRlID0gRkFMU0UpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZChlbmQgPSAuNSwgZGlyZWN0aW9uID0gLTEsIG5hLnRyYW5zbGF0ZSA9IEZBTFNFKSArCiAgbGFicyh4ID0gTlVMTCwKICAgICAgIHkgPSAiUmVzcG9uc2UgdGltZSIsCiAgICAgICBjb2xvdXIgPSAiU2Nob29sIHllYXIiLAogICAgICAgZmlsbCA9ICJTY2hvb2wgeWVhciIsCiAgICAgICBsdHkgPSAiUXVlc3Rpb24gdHlwZSIpICsKICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMSksCiAgICAgICAgIGZpbGwgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxKSwKICAgICAgICAgbHR5ID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMikpICsKICB0aGVtZV9wYXBlcgoKcF9ydF9sZXZlbF95ZWFyCgpnZ3NhdmUoIi4uL291dHB1dC9ydF9ieV9xdWVzdGlvbl90eXBlX2ZyZW5jaF9sZXZlbF95ZWFyLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gMykKZ2dzYXZlKCIuLi9vdXRwdXQvcnRfYnlfcXVlc3Rpb25fdHlwZV9mcmVuY2hfbGV2ZWxfeWVhci5lcHMiLCB3aWR0aCA9IDksIGhlaWdodCA9IDMpCmdnc2F2ZSgiLi4vb3V0cHV0L3J0X2J5X3F1ZXN0aW9uX3R5cGVfZnJlbmNoX2xldmVsX3llYXIucG5nIiwgd2lkdGggPSA5LCBoZWlnaHQgPSAzKQpgYGAKCmBgYHtyfQpwX3J0X2xldmVsX3llYXIgPC0gZ2dwbG90KHJ0X3N0cmF0X2J5X3dlZWtbY291cnNlID09ICJFbmdsaXNoIiAmIHF1ZXN0aW9uX3R5cGUgPT0gIk11bHRpcGxlXG5jaG9pY2UiICYgbGV2ZWwgIT0gIk90aGVyIixdLAogICAgICAgICAgICBhZXMoeCA9IGRveV9wb3NpeF93ZWVrX2FsaWduZWQsIHkgPSBydC8xZTMsIGdyb3VwID0gaW50ZXJhY3Rpb24oc2Nob29sX3llYXIsIHF1ZXN0aW9uX3R5cGUpLCBjb2xvdXIgPSBzY2hvb2xfeWVhciwgZmlsbCA9IHNjaG9vbF95ZWFyKSkgKwogIGZhY2V0X2dyaWQobGV2ZWwgfiB5ZWFyKSArCiAgZ2VvbV9yZWN0KHhtaW4gPSBkYXRlX3NjaG9vbHNfY2xvc2VkLCB4bWF4ID0gZGF0ZV9zY2hvb2xzX29wZW5lZCwgeW1pbiA9IDAsIHltYXggPSAxMDAwLCBmaWxsID0gImdyZXk5MiIsIGNvbG91ciA9ICJncmV5NTAiLCBsdHkgPSAyLCBhbHBoYSA9IC45KSArCiAgZ2VvbV9yaWJib24oYWVzKHltaW4gPSBydC8xZTMgLSBydF9zZS8xZTMsIHltYXggPSBydC8xZTMgKyBydF9zZS8xZTMsIGNvbG91ciA9IE5VTEwpLCBhbHBoYSA9IDAuMikgKwogIGdlb21fbGluZShhZXMobHR5ID0gcXVlc3Rpb25fdHlwZSkpICsKICBzY2FsZV94X2RhdGV0aW1lKGV4cGFuZCA9IGMoMCwgMCksCiAgICAgICAgICAgICAgICAgICBicmVha3MgPSBhcy5QT1NJWGN0KGMoCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTEwLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTEyLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTAyLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA0LTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA2LTAxIDAyOjAwOjAwIENFVCIpKSwKICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGFzLlBPU0lYY3QoYygiMjAxOS0wOS0wMSAwMjowMDowMCBDRVQiLCAiMjAyMC0wNy0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjp1bml0X2Zvcm1hdCh1bml0ID0gInMiLCBhY2N1cmFjeSA9IC4xKSkgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygxLCA0KSkgKwogIHNjYWxlX2NvbG91cl92aXJpZGlzX2QoZW5kID0gLjUsIGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKGVuZCA9IC41LCBkaXJlY3Rpb24gPSAtMSwgbmEudHJhbnNsYXRlID0gRkFMU0UpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJSZXNwb25zZSB0aW1lIiwKICAgICAgIGNvbG91ciA9ICJTY2hvb2wgeWVhciIsCiAgICAgICBmaWxsID0gIlNjaG9vbCB5ZWFyIiwKICAgICAgIGx0eSA9ICJRdWVzdGlvbiB0eXBlIikgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxKSwKICAgICAgICAgZmlsbCA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEpLAogICAgICAgICBsdHkgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAyKSkgKwogIHRoZW1lX3BhcGVyCgpwX3J0X2xldmVsX3llYXIKCmdnc2F2ZSgiLi4vb3V0cHV0L3J0X2J5X3F1ZXN0aW9uX3R5cGVfZW5nbGlzaF9sZXZlbF95ZWFyLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gMykKZ2dzYXZlKCIuLi9vdXRwdXQvcnRfYnlfcXVlc3Rpb25fdHlwZV9lbmdsaXNoX2xldmVsX3llYXIuZXBzIiwgd2lkdGggPSA5LCBoZWlnaHQgPSAzKQpnZ3NhdmUoIi4uL291dHB1dC9ydF9ieV9xdWVzdGlvbl90eXBlX2VuZ2xpc2hfbGV2ZWxfeWVhci5wbmciLCB3aWR0aCA9IDksIGhlaWdodCA9IDMpCmBgYAoKIyMjIEJ5IGxldmVsCgpgYGB7cn0KcnRfbGV2ZWxfbWVkIDwtIHJ0WywgLihydF9tZWRpYW4gPSBtZWRpYW4ocnQpKSwgYnkgPSAuKHNjaG9vbF95ZWFyLCBtY3EsIHVzZXIsIGNvdXJzZSwgbGV2ZWwsIGRveV9wb3NpeF93ZWVrKV0KCnJ0X2xldmVsX2J5X3dlZWsgPC0gcnRfbGV2ZWxfbWVkWywgLihydCA9IG1lYW4ocnRfbWVkaWFuKSwgcnRfc2UgPSBzZChydF9tZWRpYW4pL3NxcnQoLk4pKSwgYnkgPSAuKHNjaG9vbF95ZWFyLCBtY3EsIGNvdXJzZSwgbGV2ZWwsIGRveV9wb3NpeF93ZWVrKV0KYGBgCgpPdmVybGFwIHRoZSB0d28gc2Nob29sIHllYXJzOgpgYGB7cn0KcnRfbGV2ZWxfYnlfd2Vla1tzY2hvb2xfeWVhciA9PSAiMTgvMTkiLCBkb3lfcG9zaXhfd2Vla19hbGlnbmVkIDo9IGFzLlBPU0lYY3QoYXMuUE9TSVhjdChkb3lfcG9zaXhfd2VlaykgKyAzNjUqMjQqNjAqNjAsIG9yaWdpbiA9ICIxOTcwLTAxLTAxIildCnJ0X2xldmVsX2J5X3dlZWtbc2Nob29sX3llYXIgPT0gIjE5LzIwIiwgZG95X3Bvc2l4X3dlZWtfYWxpZ25lZCA6PSBhcy5QT1NJWGN0KGRveV9wb3NpeF93ZWVrKV0KYGBgCgpBZGQgcXVlc3Rpb24gdHlwZSBsYWJlbHM6CmBgYHtyfQpydF9sZXZlbF9ieV93ZWVrWywgcXVlc3Rpb25fdHlwZSA6PSBpZmVsc2UobWNxID09IFRSVUUsICJNdWx0aXBsZVxuY2hvaWNlIiwgIk9wZW5cbmFuc3dlciIpXQpgYGAKCmBgYHtyfQpydF9sZXZlbF9ieV93ZWVrWywgc2Nob29sX3llYXIgOj0gZmFjdG9yKHNjaG9vbF95ZWFyLCBsZXZlbHMgPSBjKCIxOC8xOSIsICIxOS8yMCIpKV0KYGBgCgoKUGxvdCByZXNwb25zZSB0aW1lIGJ5IHdlZWsgKG1lYW4gKy8tIDEgU0UpLgpgYGB7cn0KcF9ydF9sZXZlbCA8LSBnZ3Bsb3QocnRfbGV2ZWxfYnlfd2Vla1soKGNvdXJzZSA9PSAiRW5nbGlzaCIgJiBxdWVzdGlvbl90eXBlID09ICJNdWx0aXBsZVxuY2hvaWNlIikgfCBjb3Vyc2UgPT0gIkZyZW5jaCIpICYgbGV2ZWwgIT0gIk90aGVyIixdLAogICAgICAgICAgICBhZXMoeCA9IGRveV9wb3NpeF93ZWVrX2FsaWduZWQsIHkgPSBydC8xZTMsIGdyb3VwID0gaW50ZXJhY3Rpb24oc2Nob29sX3llYXIsIHF1ZXN0aW9uX3R5cGUpLCBjb2xvdXIgPSBzY2hvb2xfeWVhciwgZmlsbCA9IHNjaG9vbF95ZWFyKSkgKwogIGZhY2V0X2dyaWQobGV2ZWwgfiBjb3Vyc2UpICsKICBnZW9tX3JlY3QoeG1pbiA9IGRhdGVfc2Nob29sc19jbG9zZWQsIHhtYXggPSBkYXRlX3NjaG9vbHNfb3BlbmVkLCB5bWluID0gMCwgeW1heCA9IDEwMDAsIGZpbGwgPSAiZ3JleTkyIiwgY29sb3VyID0gImdyZXk1MCIsIGx0eSA9IDIsIGFscGhhID0gLjkpICsKICBnZW9tX3JpYmJvbihhZXMoeW1pbiA9IHJ0LzFlMyAtIHJ0X3NlLzFlMywgeW1heCA9IHJ0LzFlMyArIHJ0X3NlLzFlMywgY29sb3VyID0gTlVMTCksIGFscGhhID0gMC4yKSArCiAgZ2VvbV9saW5lKGFlcyhsdHkgPSBxdWVzdGlvbl90eXBlKSkgKwogIHNjYWxlX3hfZGF0ZXRpbWUoZXhwYW5kID0gYygwLCAwKSwKICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGFzLlBPU0lYY3QoYygKICAgICAgICAgICAgICAgICAgICAgIjIwMTktMTAtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMTktMTItMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDItMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDQtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDYtMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgbGltaXRzID0gYXMuUE9TSVhjdChjKCIyMDE5LTA5LTAxIDAyOjAwOjAwIENFVCIsICIyMDIwLTA3LTAxIDAyOjAwOjAwIENFVCIpKSwKICAgICAgICAgICAgICAgICAgIGRhdGVfbGFiZWxzID0gIiViIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnVuaXRfZm9ybWF0KHVuaXQgPSAicyIsIGFjY3VyYWN5ID0gLjEpKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDEsIDYpKSArCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXNfZChlbmQgPSAuNSwgZGlyZWN0aW9uID0gLTEsIG5hLnRyYW5zbGF0ZSA9IEZBTFNFKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoZW5kID0gLjUsIGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gIlJlc3BvbnNlIHRpbWUiLAogICAgICAgY29sb3VyID0gIlNjaG9vbCB5ZWFyIiwKICAgICAgIGZpbGwgPSAiU2Nob29sIHllYXIiLAogICAgICAgbHR5ID0gIlF1ZXN0aW9uIHR5cGUiKSArCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEpLAogICAgICAgICBmaWxsID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMSksCiAgICAgICAgIGx0eSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDIpKSArCiAgdGhlbWVfcGFwZXIKCnBfcnRfbGV2ZWwKCmdnc2F2ZSgiLi4vb3V0cHV0L3J0X2J5X3F1ZXN0aW9uX3R5cGVfbGV2ZWwucGRmIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA1KQpnZ3NhdmUoIi4uL291dHB1dC9ydF9ieV9xdWVzdGlvbl90eXBlX2xldmVsLmVwcyIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gNSkKZ2dzYXZlKCIuLi9vdXRwdXQvcnRfYnlfcXVlc3Rpb25fdHlwZV9sZXZlbC5wbmciLCB3aWR0aCA9IDksIGhlaWdodCA9IDUpCmBgYAoKIyMjIEJ5IHllYXIKCmBgYHtyfQpydF95ZWFyX21lZCA8LSBydFssIC4ocnRfbWVkaWFuID0gbWVkaWFuKHJ0KSksIGJ5ID0gLihzY2hvb2xfeWVhciwgbWNxLCB1c2VyLCBjb3Vyc2UsIHllYXIsIGRveV9wb3NpeF93ZWVrKV0KCnJ0X3llYXJfYnlfd2VlayA8LSBydF95ZWFyX21lZFssIC4ocnQgPSBtZWFuKHJ0X21lZGlhbiksIHJ0X3NlID0gc2QocnRfbWVkaWFuKS9zcXJ0KC5OKSksIGJ5ID0gLihzY2hvb2xfeWVhciwgbWNxLCBjb3Vyc2UsIHllYXIsIGRveV9wb3NpeF93ZWVrKV0KYGBgCgpPdmVybGFwIHRoZSB0d28gc2Nob29sIHllYXJzOgpgYGB7cn0KcnRfeWVhcl9ieV93ZWVrW3NjaG9vbF95ZWFyID09ICIxOC8xOSIsIGRveV9wb3NpeF93ZWVrX2FsaWduZWQgOj0gYXMuUE9TSVhjdChhcy5QT1NJWGN0KGRveV9wb3NpeF93ZWVrKSArIDM2NSoyNCo2MCo2MCwgb3JpZ2luID0gIjE5NzAtMDEtMDEiKV0KcnRfeWVhcl9ieV93ZWVrW3NjaG9vbF95ZWFyID09ICIxOS8yMCIsIGRveV9wb3NpeF93ZWVrX2FsaWduZWQgOj0gYXMuUE9TSVhjdChkb3lfcG9zaXhfd2VlayldCmBgYAoKQWRkIHF1ZXN0aW9uIHR5cGUgbGFiZWxzOgpgYGB7cn0KcnRfeWVhcl9ieV93ZWVrWywgcXVlc3Rpb25fdHlwZSA6PSBpZmVsc2UobWNxID09IFRSVUUsICJNdWx0aXBsZVxuY2hvaWNlIiwgIk9wZW5cbmFuc3dlciIpXQpgYGAKCmBgYHtyfQpydF95ZWFyX2J5X3dlZWtbLCBzY2hvb2xfeWVhciA6PSBmYWN0b3Ioc2Nob29sX3llYXIsIGxldmVscyA9IGMoIjE4LzE5IiwgIjE5LzIwIikpXQpgYGAKCgpQbG90IHJlc3BvbnNlIHRpbWUgYnkgd2VlayAobWVhbiArLy0gMSBTRSkuCmBgYHtyfQpwX3J0X3llYXIgPC0gZ2dwbG90KHJ0X3llYXJfYnlfd2Vla1soKGNvdXJzZSA9PSAiRW5nbGlzaCIgJiBxdWVzdGlvbl90eXBlID09ICJNdWx0aXBsZVxuY2hvaWNlIikgfCBjb3Vyc2UgPT0gIkZyZW5jaCIpICYgeWVhciAhPSAiT3RoZXIiLF0sCiAgICAgICAgICAgIGFlcyh4ID0gZG95X3Bvc2l4X3dlZWtfYWxpZ25lZCwgeSA9IHJ0LzFlMywgZ3JvdXAgPSBpbnRlcmFjdGlvbihzY2hvb2xfeWVhciwgcXVlc3Rpb25fdHlwZSksIGNvbG91ciA9IHNjaG9vbF95ZWFyLCBmaWxsID0gc2Nob29sX3llYXIpKSArCiAgZmFjZXRfZ3JpZCh5ZWFyIH4gY291cnNlKSArCiAgZ2VvbV9yZWN0KHhtaW4gPSBkYXRlX3NjaG9vbHNfY2xvc2VkLCB4bWF4ID0gZGF0ZV9zY2hvb2xzX29wZW5lZCwgeW1pbiA9IDAsIHltYXggPSAxMDAwLCBmaWxsID0gImdyZXk5MiIsIGNvbG91ciA9ICJncmV5NTAiLCBsdHkgPSAyLCBhbHBoYSA9IC45KSArCiAgZ2VvbV9yaWJib24oYWVzKHltaW4gPSBydC8xZTMgLSBydF9zZS8xZTMsIHltYXggPSBydC8xZTMgKyBydF9zZS8xZTMsIGNvbG91ciA9IE5VTEwpLCBhbHBoYSA9IDAuMikgKwogIGdlb21fbGluZShhZXMobHR5ID0gcXVlc3Rpb25fdHlwZSkpICsKICBzY2FsZV94X2RhdGV0aW1lKGV4cGFuZCA9IGMoMCwgMCksCiAgICAgICAgICAgICAgICAgICBicmVha3MgPSBhcy5QT1NJWGN0KGMoCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTEwLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTEyLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTAyLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA0LTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA2LTAxIDAyOjAwOjAwIENFVCIpKSwKICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGFzLlBPU0lYY3QoYygiMjAxOS0wOS0wMSAwMjowMDowMCBDRVQiLCAiMjAyMC0wNy0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjp1bml0X2Zvcm1hdCh1bml0ID0gInMiLCBhY2N1cmFjeSA9IC4xKSkgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygxLCA0KSkgKwogIHNjYWxlX2NvbG91cl92aXJpZGlzX2QoZW5kID0gLjUsIGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKGVuZCA9IC41LCBkaXJlY3Rpb24gPSAtMSwgbmEudHJhbnNsYXRlID0gRkFMU0UpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJSZXNwb25zZSB0aW1lIiwKICAgICAgIGNvbG91ciA9ICJTY2hvb2wgeWVhciIsCiAgICAgICBmaWxsID0gIlNjaG9vbCB5ZWFyIiwKICAgICAgIGx0eSA9ICJRdWVzdGlvbiB0eXBlIikgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxKSwKICAgICAgICAgZmlsbCA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEpLAogICAgICAgICBsdHkgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAyKSkgKwogIHRoZW1lX3BhcGVyCgpwX3J0X3llYXIKCmdnc2F2ZSgiLi4vb3V0cHV0L3J0X2J5X3F1ZXN0aW9uX3R5cGVfeWVhci5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDUpCmdnc2F2ZSgiLi4vb3V0cHV0L3J0X2J5X3F1ZXN0aW9uX3R5cGVfeWVhci5lcHMiLCB3aWR0aCA9IDksIGhlaWdodCA9IDUpCmdnc2F2ZSgiLi4vb3V0cHV0L3J0X2J5X3F1ZXN0aW9uX3R5cGVfeWVhci5wbmciLCB3aWR0aCA9IDksIGhlaWdodCA9IDUpCmBgYAoKCiMjIyBSZWdyZXNzaW9uIG1vZGVsCgpgYGB7cn0KcnRfbW9kZWxfZGF0IDwtIHJ0WywgLihydF9tZWRpYW4gPSBtZWRpYW4ocnQpKSwgYnkgPSAuKGNvdXJzZSwgc2Nob29sX3llYXIsIHBlcmlvZCwgZG95X3Bvc2l4LCBtY3EsIHVzZXIpXQpgYGAKCkZpdCBhIGdlbmVyYWxpc2VkIGxpbmVhciBtaXhlZCBlZmZlY3RzIG1vZGVsIChhc3N1bWluZyBhIEdhbW1hIGRpc3RyaWJ1dGlvbiBmb3IgUlQgYW5kIGFuIGlkZW50aXR5IGxpbmsgZnVuY3Rpb247IExvICYgQW5kcmV3LCAyMDE1KSB0byB0aGUgZGFpbHkgbWVkaWFuIFJUOgpgYGB7cn0KaWYoIWZpbGUuZXhpc3RzKCIuLi9vdXRwdXQvbV9ydF9maXQucmRzIikpIHsKICBtX3J0IDwtIGdsbWVyKHJ0X21lZGlhbiB+IHBlcmlvZCpzY2hvb2xfeWVhciptY3EgKyAoMSB8IHVzZXIpICsgKDEgfCBjb3Vyc2UpLAogICAgICAgICAgICAgICAgIGRhdGEgPSBydF9tb2RlbF9kYXRbKGNvdXJzZSA9PSAiRW5nbGlzaCIgJiBtY3EgPT0gVFJVRSkgfCBjb3Vyc2UgPT0gIkZyZW5jaCIsXSwKICAgICAgICAgICAgICAgICBmYW1pbHkgPSBHYW1tYShsaW5rID0gImlkZW50aXR5IiksCiAgICAgICAgICAgICAgICAgbkFHUSA9IDAsCiAgICAgICAgICAgICAgICAgY29udHJvbCA9IGdsbWVyQ29udHJvbChvcHRpbWl6ZXIgPSAiYm9ieXFhIiwgb3B0Q3RybCA9IGxpc3QobWF4ZnVuID0gMWU2KSkpCiAgc2F2ZVJEUyhtX3J0LCAiLi4vb3V0cHV0L21fcnRfZml0LnJkcyIpCn0gZWxzZSB7CiAgbV9ydCA8LSByZWFkUkRTKCIuLi9vdXRwdXQvbV9ydF9maXQucmRzIikKfQoKbV9ydF9zdW1tYXJ5IDwtIHN1bW1hcnkobV9ydCkKbV9ydF9zdW1tYXJ5CmBgYAoKU2F2ZSBjb2VmZmljaWVudHMgYXMgYSB0YWJsZSBmb3IgaW4gdGhlIHBhcGVyOgpgYGB7cn0KbV9ydF9jb2VmIDwtIGFzLmRhdGEuZnJhbWUobV9ydF9zdW1tYXJ5JGNvZWZmaWNpZW50cykKc2V0RFQobV9ydF9jb2VmLCBrZWVwLnJvd25hbWVzID0gVFJVRSkKbV9ydF9jb2VmJHJuIDwtIGMoIkludGVyY2VwdCBcXHNtYWxseyhQZXJpb2Q6IHByZS1sb2NrZG93biwgU2Nob29sIHllYXI6IDE5LzIwLCBRdWVzdGlvbiB0eXBlOiBvcGVuIGFuc3dlcil9IiwKICAgICAgICAgICAgICAgICAgICJQZXJpb2Q6IGxvY2tkb3duIiwKICAgICAgICAgICAgICAgICAgICJQZXJpb2Q6IHBvc3QtbG9ja2Rvd24iLAogICAgICAgICAgICAgICAgICAgIlNjaG9vbCB5ZWFyOiAxOC8xOSIsCiAgICAgICAgICAgICAgICAgICAiUXVlc3Rpb24gdHlwZTogbXVsdGlwbGUgY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICJQZXJpb2Q6IGxvY2tkb3duICRcXHRpbWVzJCBTY2hvb2wgeWVhcjogMTgvMTkiLAogICAgICAgICAgICAgICAgICAgIlBlcmlvZDogcG9zdC1sb2NrZG93biAkXFx0aW1lcyQgU2Nob29sIHllYXI6IDE4LzE5IiwKICAgICAgICAgICAgICAgICAgICJQZXJpb2Q6IGxvY2tkb3duICRcXHRpbWVzJCBRdWVzdGlvbiB0eXBlOiBtdWx0aXBsZSBjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgIlBlcmlvZDogcG9zdC1sb2NrZG93biAkXFx0aW1lcyQgUXVlc3Rpb24gdHlwZTogbXVsdGlwbGUgY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICJTY2hvb2wgeWVhcjogMTgvMTkgJFxcdGltZXMkIFF1ZXN0aW9uIHR5cGU6IG11bHRpcGxlIGNob2ljZSIsCiAgICAgICAgICAgICAgICAgICAiUGVyaW9kOiBsb2NrZG93biAkXFx0aW1lcyQgU2Nob29sIHllYXI6IDE4LzE5ICRcXHRpbWVzJCBRdWVzdGlvbiB0eXBlOiBtdWx0aXBsZSBjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgIlBlcmlvZDogcG9zdC1sb2NrZG93biAkXFx0aW1lcyQgU2Nob29sIHllYXI6IDE4LzE5ICRcXHRpbWVzJCBRdWVzdGlvbiB0eXBlOiBtdWx0aXBsZSBjaG9pY2UiKQoKIyBGb3JtYXQgcC12YWx1ZXMKbV9ydF9jb2VmJGBQcig+fHp8KWAgPC0gZm9ybWF0LnB2YWwobV9ydF9jb2VmJGBQcig+fHp8KWAsIGVwcyA9IC4wMDEsIGRpZ2l0cyA9IDMsIGZsYWcgPSAiMCIpCm1fcnRfY29lZiRgUHIoPnx6fClgIDwtIHN1YignXig8KT8wWy5dJywgJ1xcMS4nLCBtX3J0X2NvZWYkYFByKD58enwpYCkgIyBSZW1vdmUgbGVhZGluZyB6ZXJvCgpjYXQoa25pdHI6OmthYmxlKG1fcnRfY29lZiwKICAgICAgICAgICAgICAgICBhbGlnbiA9IGMoImwiLCJyIiwgInIiLCAiciIsICJyIiksCiAgICAgICAgICAgICAgICAgZGlnaXRzID0gYyhOQSwgMywgMywgMiwgTkEpLAogICAgICAgICAgICAgICAgIGNvbC5uYW1lcyA9IGMoIkVmZmVjdCIsICIkYiQiLCAiU0UiLCAiJHokIiwgIiRwJCIpLAogICAgICAgICAgICAgICAgIGZvcm1hdCA9ICJsYXRleCIsCiAgICAgICAgICAgICAgICAgYm9va3RhYnMgPSBUUlVFLAogICAgICAgICAgICAgICAgIGVzY2FwZSA9IEZBTFNFKSwKICAgIGZpbGUgPSAiLi4vb3V0cHV0L21fcnRfdGFibGUudGV4IikKYGBgCgpWaXN1YWxpc2UgdGhlIG1vZGVsIGZpdDoKYGBge3J9CnJ0X2ZpdCA8LSBleHBhbmQuZ3JpZChwZXJpb2QgPSBjKCJwcmUtbG9ja2Rvd24iLCAiZHVyaW5nLWxvY2tkb3duIiwgInBvc3QtbG9ja2Rvd24iKSwgc2Nob29sX3llYXIgPSBjKCIxOC8xOSIsICIxOS8yMCIpLCBtY3EgPSBjKFRSVUUsIEZBTFNFKSkKcnRfZml0IDwtIGNiaW5kKHJ0X2ZpdCwgcnQgPSBwcmVkaWN0KG1fcnQsIHR5cGUgPSAicmVzcG9uc2UiLCByZS5mb3JtID0gTkEsIG5ld2RhdGEgPSBydF9maXQpKQpydF9maXQKCmdncGxvdChydF9maXQsIGFlcyh4ID0gcGVyaW9kLCB5ID0gcnQsIGNvbG91ciA9IHNjaG9vbF95ZWFyLCBsdHkgPSBtY3EsIGdyb3VwID0gaW50ZXJhY3Rpb24obWNxLCBzY2hvb2xfeWVhcikpKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMTUwMCwgNDAwMCkpICsKICB0aGVtZV9wYXBlcgpgYGAKCkVtcGlyaWNhbCBtZWFuczoKYGBge3J9CnJ0X21lYW4gPC0gcnRfbW9kZWxfZGF0Wyhjb3Vyc2UgPT0gIkVuZ2xpc2giICYgbWNxID09IFRSVUUpIHwgY291cnNlID09ICJGcmVuY2giLCAuKHJ0ID0gbWVhbihydF9tZWRpYW4pKSwgYnkgPSAuKHBlcmlvZCwgc2Nob29sX3llYXIsIG1jcSwgdXNlciwgY291cnNlKV1bLCAuKHJ0ID0gbWVhbihydCksIHJ0X3NkID0gc2QocnQpKSwgYnkgPSAuKHBlcmlvZCwgc2Nob29sX3llYXIsIG1jcSwgY291cnNlKV0KcnRfbWVhblssIHNjaG9vbF95ZWFyIDo9IGZhY3RvcihzY2hvb2xfeWVhciwgbGV2ZWxzID0gYygiMTgvMTkiLCAiMTkvMjAiKSldCnJ0X21lYW4KYGBgCgpgYGB7cn0KZ2dwbG90KHJ0X21lYW4sIGFlcyh4ID0gcGVyaW9kLCB5ID0gcnQsIGNvbG91ciA9IHNjaG9vbF95ZWFyLCBsdHkgPSBtY3EsIGdyb3VwID0gaW50ZXJhY3Rpb24obWNxLCBzY2hvb2xfeWVhcikpKSArCiAgZmFjZXRfZ3JpZCh+IGNvdXJzZSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDE1MDAsIDMwMDApKSArCiAgdGhlbWVfcGFwZXIKYGBgCgoKIyMgQ29tYmluYXRpb24gcGxvdAoKYGBge3J9CnBfbGVnZW5kIDwtIGdldF9sZWdlbmQocF9hY2MpCgpwX2FjYyA8LSBwX2FjYyArCiAgZ3VpZGVzKGNvbG91ciA9IEZBTFNFLCBmaWxsID0gRkFMU0UsIGx0eSA9IEZBTFNFKQoKcF9ydCA8LSBwX3J0ICsKICBndWlkZXMoY29sb3VyID0gRkFMU0UsIGZpbGwgPSBGQUxTRSwgbHR5ID0gRkFMU0UpCmBgYAoKQ29tYmluZSBwbG90czoKYGBge3J9CnBsb3RfZ3JpZCgKICBwbG90X2dyaWQocF9hY2MsIHBfcnQsCiAgICAgICAgICAgIG5jb2wgPSAxLAogICAgICAgICAgICBsYWJlbHMgPSBjKCJBIiwgIkIiKSksCiAgcF9sZWdlbmQsCiAgcmVsX3dpZHRocyA9IGMoMSwgLjIpCikKCmdnc2F2ZSgiLi4vb3V0cHV0L2NvbWJpX2FjY19ydC5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDMuNSkKZ2dzYXZlKCIuLi9vdXRwdXQvY29tYmlfYWNjX3J0LmVwcyIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gMy41KQpnZ3NhdmUoIi4uL291dHB1dC9jb21iaV9hY2NfcnQucG5nIiwgd2lkdGggPSA5LCBoZWlnaHQgPSAzLjUpCmBgYAoKCgojIExlYXJuaW5nIHByb2dyZXNzCgpHZXQgdGhlIHVuaXF1ZSBib29rIGNoYXB0ZXIgSURzIG9uIGVhY2ggZGF5OgpgYGB7cn0KZGIgPC0gZGJfY29ubmVjdCgpCgpwcm9ncmVzcyA8LSBkYkdldFF1ZXJ5KGRiLAogICAgICAgICAgICAgICAgICAgICAgICJTRUxFQ1QgRElTVElOQ1Qgci5ib29rX2luZm9faWQgQVMgJ2Jvb2tfaW5mb19pZCcsCiAgICAgICAgICAgICAgICAgICAgICAgIHIubWV0aG9kIEFTICdtZXRob2QnLAogICAgICAgICAgICAgICAgICAgICAgICBEQVRFKHIuZGF0ZSArIDM2MDAsICd1bml4ZXBvY2gnKSBBUyAnZG95JywKICAgICAgICAgICAgICAgICAgICAgICAgQ09VTlQoKikgQVMgJ3RyaWFscycKICAgICAgICAgICAgICAgICAgICAgICAgRlJPTSAncmVzcG9uc2VzX25vZHVwbGljYXRlcycgcgogICAgICAgICAgICAgICAgICAgICAgICBHUk9VUCBCWSByLm1ldGhvZCwKICAgICAgICAgICAgICAgICAgICAgICAgci5ib29rX2luZm9faWQsCiAgICAgICAgICAgICAgICAgICAgICAgIERBVEUoci5kYXRlICsgMzYwMCwgJ3VuaXhlcG9jaCcpOyIKICAgICAgICAgICAgICAgICAgICAgICApCgpkYl9kaXNjb25uZWN0KGRiKQoKc2V0RFQocHJvZ3Jlc3MpCmBgYAoKSm9pbiB3aXRoIHRoZSBib29rIGNoYXB0ZXIgaW5mb3JtYXRpb246CmBgYHtyfQpkYiA8LSBkYl9jb25uZWN0KCkKYm9va19pbmZvIDwtIGRiR2V0UXVlcnkoZGIsICJTRUxFQ1QgRElTVElOQ1QgKiBGUk9NICdib29rX2luZm8nIikKZGJfZGlzY29ubmVjdChkYikKCnNldERUKGJvb2tfaW5mbykKYGBgCgpgYGB7cn0KcHJvZ3Jlc3NbYm9va19pbmZvW2Jvb2tfdHlwZSA9PSAiSG9vZmRib2VrIixdLCBvbiAgPSAiYm9va19pbmZvX2lkIiwgYygibWV0aG9kX2dyb3VwIiwgImJvb2tfdGl0bGUiLCAiY2hhcHRlciIpIDo9IC4oaS5tZXRob2RfZ3JvdXAsIGkuYm9va190aXRsZSwgaS5jaGFwdGVyKV0KYGBgCgpBZGQgc2Vuc2libGUgY291cnNlIG5hbWVzOgpgYGB7cn0KcHJvZ3Jlc3NbLCBjb3Vyc2UgOj0gaWZlbHNlKG1ldGhvZCA9PSAiR3JhbmRlcyBMaWduZXMiLCAiRnJlbmNoIiwgaWZlbHNlKG1ldGhvZCA9PSAiU3RlcHBpbmcgU3RvbmVzIiwgIkVuZ2xpc2giLCAiR2VybWFuIikpXQpgYGAKCkFkZCBhIHNjaG9vbCB5ZWFyIGNvbHVtbiAoY3V0b2ZmIGRhdGU6IDEgQXVndXN0KToKYGBge3J9CnByb2dyZXNzWywgZG95X3Bvc2l4IDo9IGFzLlBPU0lYY3QoZG95KV0KcHJvZ3Jlc3NbLCBzY2hvb2xfeWVhciA6PSBpZmVsc2UoZG95X3Bvc2l4IDwgIjIwMTktMDgtMDEiLCAiMTgvMTkiLCAiMTkvMjAiKV0KYGBgCgpDb25zb2xpZGF0ZSBjb3VudCBieSBkYXkgYW5kIGNoYXB0ZXI6CmBgYHtyfQpwcm9ncmVzc19ieV9kYXkgPC0gcHJvZ3Jlc3NbLCAuKHRyaWFscyA9IHN1bSh0cmlhbHMpKSwgYnkgPSAuKHNjaG9vbF95ZWFyLCBkb3lfcG9zaXgsIGNvdXJzZSwgbWV0aG9kX2dyb3VwLCBib29rX3RpdGxlLCBjaGFwdGVyKV0KYGBgCgoKU2ltcGxpZnkgbGV2ZWwgbmFtZXM6CmBgYHtyfQojIEtlZXAgYWxsIGRpc3RpbmN0aW9ucwpwcm9ncmVzc19ieV9kYXlbLCBib29rX3RpdGxlX3NpbXBsZSA6PSBzdHJpbmdyOjpzdHJfc3ViKGJvb2tfdGl0bGUsIDMsIC0xMCldCnByb2dyZXNzX2J5X2RheVssIGJvb2tfdGl0bGVfc2ltcGxlIDo9IGZhY3Rvcihib29rX3RpdGxlX3NpbXBsZSwgbGV2ZWxzID0gYygidm1ibyBiL2x3b28iLCAidm1ibyBiIiwgInZtYm8gYmsiLCAidm1ibyBrIiwgInZtYm8ga2d0IiwgInZtYm8tZ3QiLCAidm1ibyBndCIsICJ2bWJvLWd0L2hhdm8iLCAidm1ibyAodClodiIsICJoYXZvIiwgImhhdm8gdndvIiwgInZ3byIpKV0KCiMgU2ltcGxpZnkgdG8gdGhyZWUgbGV2ZWxzCnByb2dyZXNzX2J5X2RheVssIGxldmVsIDo9IGRwbHlyOjpjYXNlX3doZW4oCiAgZ3JlcGwoICJodiIsIGJvb2tfdGl0bGUpIH4gIkdlbmVyYWwgc2Vjb25kYXJ5IChoYXZvKSIsCiAgZ3JlcGwoInZtYm8iLCBib29rX3RpdGxlKSB+ICJQcmUtdm9jYXRpb25hbCAodm1ibykiLAogIGdyZXBsKCJoYXZvIiwgYm9va190aXRsZSkgfiAiR2VuZXJhbCBzZWNvbmRhcnkgKGhhdm8pIiwKICBncmVwbCgidndvIiwgYm9va190aXRsZSkgfiAiUHJlLXVuaXZlcnNpdHkgKHZ3bykiLAogIFRSVUUgfiAiT3RoZXIiKV0KcHJvZ3Jlc3NfYnlfZGF5WywgbGV2ZWwgOj0gZmFjdG9yKGxldmVsLCBsZXZlbHMgPSBjKCJPdGhlciIsICJQcmUtdm9jYXRpb25hbCAodm1ibykiLCAiR2VuZXJhbCBzZWNvbmRhcnkgKGhhdm8pIiwgIlByZS11bml2ZXJzaXR5ICh2d28pIikpXQpgYGAKClNpbXBsaWZ5IHllYXIgbmFtZXM6CmBgYHtyfQpwcm9ncmVzc19ieV9kYXlbLCB5ZWFyIDo9IGRwbHlyOjpjYXNlX3doZW4oCiAgbWV0aG9kX2dyb3VwID09ICJMZWVyamFhciAxICg1ZSBFZC4pIiB+ICJZZWFyIDEiLAogIG1ldGhvZF9ncm91cCA9PSAiTGVlcmphYXIgMiAoNWUgRWQuKSIgfiAiWWVhciAyIiwKICBtZXRob2RfZ3JvdXAgPT0gIkxlZXJqYWFyIDMgKDVlIEVkLikiIH4gIlllYXIgMyIsCiAgbWV0aG9kX2dyb3VwID09ICJMZWVyamFhciAzLzQgKDVlIEVkLikiIH4gIlllYXIgMy80IiwKICBtZXRob2RfZ3JvdXAgPT0gIkxlZXJqYWFyIDQgKDVlIEVkLikiIH4gIlllYXIgNCIsCiAgbWV0aG9kX2dyb3VwID09ICJUd2VlZGUgRmFzZSAoNmUgRWQuKSIgfiAiVHdlZWRlIEZhc2UiLAogIFRSVUUgfiAiT3RoZXIiKV0KYGBgCgoKU2ltcGxpZnkgY2hhcHRlciBuYW1lczoKYGBge3J9CiMgSW4gbW9zdCBjYXNlcywgdGhlIGNoYXB0ZXIgbmFtZSBzdGFydHMgd2l0aCBhIG51bWJlcgpwcm9ncmVzc19ieV9kYXlbLCBjaGFwdGVyX3NpbXBsZSA6PSBmYWN0b3IoYXMubnVtZXJpYyhzdHJpbmdyOjpzdHJfZXh0cmFjdChjaGFwdGVyLCAiXlxcZHsxLDJ9IikpKV0KCiMgUmVtYWluaW5nIGNhc2VzOgp1bmlxdWUocHJvZ3Jlc3NfYnlfZGF5W2lzLm5hKGNoYXB0ZXJfc2ltcGxlKSxdJGNoYXB0ZXIpCgojIENvbWJpbmUgdGhlc2UgY2hhcHRlcnMgaW50byBhbiAib3RoZXIiIGNhdGVnb3J5CnByb2dyZXNzX2J5X2RheVtpcy5uYShjaGFwdGVyX3NpbXBsZSksIGNoYXB0ZXJfc2ltcGxlIDo9ICJPIl0KYGBgCgoKQWxpZ24gc2Nob29sIHllYXJzOgpgYGB7cn0KcHJvZ3Jlc3NfYnlfZGF5W3NjaG9vbF95ZWFyID09ICIxOC8xOSIsIGRveV9wb3NpeF9hbGlnbmVkIDo9IGFzLlBPU0lYY3QoZG95X3Bvc2l4ICsgMzY1KjI0KjYwKjYwLCBvcmlnaW4gPSAiMTk3MC0wMS0wMSIpXQpwcm9ncmVzc19ieV9kYXlbc2Nob29sX3llYXIgPT0gIjE5LzIwIiwgZG95X3Bvc2l4X2FsaWduZWQgOj0gZG95X3Bvc2l4XQpgYGAKClVzZSBjdXQuRGF0ZSgpIHRvIGJpbiBkYXRlcyBieSB3ZWVrIGFuZCBtb250aC4gRWFjaCBkYXkgaXMgYXNzaWduZWQgdGhlIGRhdGUgb2YgdGhlIG1vc3QgcmVjZW50IE1vbmRheS4KYGBge3J9CnByb2dyZXNzX2J5X2RheVssIGRveV9wb3NpeF9hbGlnbmVkX3dlZWsgOj0gY3V0LlBPU0lYdChkb3lfcG9zaXhfYWxpZ25lZCwgIndlZWsiKV0KcHJvZ3Jlc3NfYnlfZGF5WywgZG95X3Bvc2l4X2FsaWduZWRfbW9udGggOj0gY3V0LlBPU0lYdChkb3lfcG9zaXhfYWxpZ25lZCwgIm1vbnRoIildCmBgYAoKQ2FsY3VsYXRlIHByb3BvcnRpb25zIGJ5IHdlZWsgYW5kIG1vbnRoOgpgYGB7cn0KcHJvZ3Jlc3NfYnlfd2VlayA8LSBwcm9ncmVzc19ieV9kYXlbLCAuKHRyaWFscyA9IHN1bSh0cmlhbHMpKSwgYnkgPSAuKHNjaG9vbF95ZWFyLCBkb3lfcG9zaXhfYWxpZ25lZF93ZWVrLCBjb3Vyc2UsIGxldmVsLCB5ZWFyLCBjaGFwdGVyX3NpbXBsZSldCnByb2dyZXNzX2J5X3dlZWtbLCBwcm9wIDo9IHRyaWFscy9zdW0odHJpYWxzKSwgYnkgPSAuKHNjaG9vbF95ZWFyLCBkb3lfcG9zaXhfYWxpZ25lZF93ZWVrLCBjb3Vyc2UsIGxldmVsLCB5ZWFyKV0KCnByb2dyZXNzX2J5X21vbnRoIDwtIHByb2dyZXNzX2J5X2RheVssIC4odHJpYWxzID0gc3VtKHRyaWFscykpLCBieSA9IC4oc2Nob29sX3llYXIsIGRveV9wb3NpeF9hbGlnbmVkX21vbnRoLCBjb3Vyc2UsIGxldmVsLCB5ZWFyLCBjaGFwdGVyX3NpbXBsZSldCnByb2dyZXNzX2J5X21vbnRoWywgcHJvcCA6PSB0cmlhbHMvc3VtKHRyaWFscyksIGJ5ID0gLihzY2hvb2xfeWVhciwgZG95X3Bvc2l4X2FsaWduZWRfbW9udGgsIGNvdXJzZSwgbGV2ZWwsIHllYXIpXQpgYGAKCmBgYHtyfQpzZXRvcmRlcihwcm9ncmVzc19ieV93ZWVrLCBjaGFwdGVyX3NpbXBsZSkKc2V0b3JkZXIocHJvZ3Jlc3NfYnlfbW9udGgsIGNoYXB0ZXJfc2ltcGxlKQpgYGAKCgojIyMgRnJlbmNoCmBgYHtyfQpwX2ZyZW5jaF95MSA8LSBnZ3Bsb3QocHJvZ3Jlc3NfYnlfd2Vla1tjb3Vyc2UgPT0gIkZyZW5jaCIgJiB5ZWFyID09ICJZZWFyIDEiXSwgYWVzKHggPSBhcy5QT1NJWGN0KGRveV9wb3NpeF9hbGlnbmVkX3dlZWspLCB5ID0gcHJvcCwgZmlsbCA9IGNoYXB0ZXJfc2ltcGxlLCBncm91cCA9IHNjaG9vbF95ZWFyKSkgKwogIGZhY2V0X2dyaWQoc2Nob29sX3llYXIgfiBsZXZlbCkgKwogIGdlb21fY29sKGFscGhhID0gMC43NSwgd2lkdGggPSA3KjI0KjYwKjYwLCBjb2xvdXIgPSBOQSkgKwogIGdlb21fcmVjdCh4bWluID0gZGF0ZV9zY2hvb2xzX2Nsb3NlZCwgeG1heCA9IGRhdGVfc2Nob29sc19vcGVuZWQsIHltaW4gPSAtMC4wMSwgeW1heCA9IDEuMDEsIGZpbGwgPSBOQSwgY29sb3VyID0gImJsYWNrIiwgbHR5ID0gMikgKwogIHNjYWxlX3hfZGF0ZXRpbWUoZXhwYW5kID0gYygwLCAwKSwgCiAgICAgICAgICAgICAgICAgICBicmVha3MgPSBhcy5QT1NJWGN0KGMoCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTEwLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTEyLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTAyLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA0LTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA2LTAxIDAyOjAwOjAwIENFVCIpKSwKICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGFzLlBPU0lYY3QoYygiMjAxOS0wOS0wMSAwMjowMDowMCBDRVQiLCAiMjAyMC0wNy0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdCgpLCBsaW1pdHMgPSBjKDAsMSksIGJyZWFrcyA9IGMoMCwgLjUgLCAxKSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAyKSkgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gIlNoYXJlIG9mIHRyaWFscyIsCiAgICAgICBmaWxsID0gIkNoYXB0ZXIiKSArCiAgdGhlbWVfcGFwZXIKCnBfZnJlbmNoX3kyIDwtIGdncGxvdChwcm9ncmVzc19ieV93ZWVrW2NvdXJzZSA9PSAiRnJlbmNoIiAmIHllYXIgPT0gIlllYXIgMiJdLCBhZXMoeCA9IGFzLlBPU0lYY3QoZG95X3Bvc2l4X2FsaWduZWRfd2VlayksIHkgPSBwcm9wLCBmaWxsID0gY2hhcHRlcl9zaW1wbGUsIGdyb3VwID0gc2Nob29sX3llYXIpKSArCiAgZmFjZXRfZ3JpZChzY2hvb2xfeWVhciB+IGxldmVsKSArCiAgZ2VvbV9jb2woYWxwaGEgPSAwLjc1LCB3aWR0aCA9IDcqMjQqNjAqNjAsIGNvbG91ciA9IE5BKSArCiAgZ2VvbV9yZWN0KHhtaW4gPSBkYXRlX3NjaG9vbHNfY2xvc2VkLCB4bWF4ID0gZGF0ZV9zY2hvb2xzX29wZW5lZCwgeW1pbiA9IC0wLjAxLCB5bWF4ID0gMS4wMSwgZmlsbCA9IE5BLCBjb2xvdXIgPSAiYmxhY2siLCBsdHkgPSAyKSArCiAgc2NhbGVfeF9kYXRldGltZShleHBhbmQgPSBjKDAsIDApLCAKICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGFzLlBPU0lYY3QoYygKICAgICAgICAgICAgICAgICAgICAgIjIwMTktMTAtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMTktMTItMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDItMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDQtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDYtMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgbGltaXRzID0gYXMuUE9TSVhjdChjKCIyMDE5LTA5LTAxIDAyOjAwOjAwIENFVCIsICIyMDIwLTA3LTAxIDAyOjAwOjAwIENFVCIpKSwKICAgICAgICAgICAgICAgICAgIGRhdGVfbGFiZWxzID0gIiViIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KCksIGxpbWl0cyA9IGMoMCwxKSwgYnJlYWtzID0gYygwLCAuNSAsIDEpKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoZGlyZWN0aW9uID0gLTEsIG5hLnRyYW5zbGF0ZSA9IEZBTFNFKSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQobmNvbCA9IDIpKSArCiAgbGFicyh4ID0gTlVMTCwKICAgICAgIHkgPSAiU2hhcmUgb2YgdHJpYWxzIiwKICAgICAgIGZpbGwgPSAiQ2hhcHRlciIpICsKICB0aGVtZV9wYXBlcgoKcF9mcmVuY2hfeTMgPC0gZ2dwbG90KHByb2dyZXNzX2J5X3dlZWtbY291cnNlID09ICJGcmVuY2giICYgeWVhciA9PSAiWWVhciAzLzQiXSwgYWVzKHggPSBhcy5QT1NJWGN0KGRveV9wb3NpeF9hbGlnbmVkX3dlZWspLCB5ID0gcHJvcCwgZmlsbCA9IGNoYXB0ZXJfc2ltcGxlLCBncm91cCA9IHNjaG9vbF95ZWFyKSkgKwogIGZhY2V0X2dyaWQoc2Nob29sX3llYXIgfiBsZXZlbCkgKwogIGdlb21fY29sKGFscGhhID0gMC43NSwgd2lkdGggPSA3KjI0KjYwKjYwLCBjb2xvdXIgPSBOQSkgKwogIGdlb21fcmVjdCh4bWluID0gZGF0ZV9zY2hvb2xzX2Nsb3NlZCwgeG1heCA9IGRhdGVfc2Nob29sc19vcGVuZWQsIHltaW4gPSAtMC4wMSwgeW1heCA9IDEuMDEsIGZpbGwgPSBOQSwgY29sb3VyID0gImJsYWNrIiwgbHR5ID0gMikgKwogIHNjYWxlX3hfZGF0ZXRpbWUoZXhwYW5kID0gYygwLCAwKSwgCiAgICAgICAgICAgICAgICAgICBicmVha3MgPSBhcy5QT1NJWGN0KGMoCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTEwLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTEyLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTAyLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA0LTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA2LTAxIDAyOjAwOjAwIENFVCIpKSwKICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGFzLlBPU0lYY3QoYygiMjAxOS0wOS0wMSAwMjowMDowMCBDRVQiLCAiMjAyMC0wNy0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdCgpLCBsaW1pdHMgPSBjKDAsMSksIGJyZWFrcyA9IGMoMCwgLjUgLCAxKSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAyKSkgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gIlNoYXJlIG9mIHRyaWFscyIsCiAgICAgICBmaWxsID0gIkNoYXB0ZXIiKSArCiAgdGhlbWVfcGFwZXIKCnBfcHJvZ3Jlc3NfZnJlbmNoIDwtIHBsb3RfZ3JpZChwX2ZyZW5jaF95MSwgcF9mcmVuY2hfeTIsIHBfZnJlbmNoX3kzLCAKICAgICAgICAgIG5jb2wgPSAxLAogICAgICAgICAgYWxpZ24gPSAiaHYiLCBheGlzID0gInRibHIiLAogICAgICAgICAgbGFiZWxzID0gYygiWWVhciAxIiwgIlllYXIgMiIsICJZZWFyIDMvNCIpLAogICAgICAgICAgaGp1c3QgPSAtMC4xLAogICAgICAgICAgc2NhbGUgPSAuOTUpCgpwX3Byb2dyZXNzX2ZyZW5jaAoKZ2dzYXZlKCIuLi9vdXRwdXQvcHJvZ3Jlc3NfZnJlbmNoLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKZ2dzYXZlKCIuLi9vdXRwdXQvcHJvZ3Jlc3NfZnJlbmNoLmVwcyIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKZ2dzYXZlKCIuLi9vdXRwdXQvcHJvZ3Jlc3NfZnJlbmNoLnBuZyIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gNikKYGBgCgoKRGlkIHRoZSBzaGFyZSBvZiB0cmlhbHMgY2hhbmdlIGJldHdlZW4gc2Nob29sIHllYXJzPwpXZSBjYW4gc2ltcGxpZnkgdGhlIGFuYWx5c2lzIGJ5IGFnZ3JlZ2F0aW5nIG92ZXIgdGhlIHdob2xlIGxvY2tkb3duIHBlcmlvZC4KYGBge3J9CnByb2dyZXNzX2xvY2tkb3duIDwtIHByb2dyZXNzX2J5X2RheVtiZXR3ZWVuKGRveV9wb3NpeF9hbGlnbmVkLCBkYXRlX3NjaG9vbHNfY2xvc2VkLCBkYXRlX3NjaG9vbHNfb3BlbmVkKSwgLih0cmlhbHMgPSBzdW0odHJpYWxzKSksIGJ5ID0gLihzY2hvb2xfeWVhciwgY291cnNlLCBsZXZlbCwgeWVhciwgY2hhcHRlcl9zaW1wbGUpXQoKIyBGaWxsIGluIG1pc3Npbmcgcm93cyAob2NjdXJzIHdoZW4gY2hhcHRlciB3YXMgb25seSBzdHVkaWVkIGluIG9uZSBvZiB0aGUgdHdvIHllYXJzKQpwcm9ncmVzc19sb2NrZG93biA8LSBhcy5kYXRhLnRhYmxlKHRpZHlyOjpjb21wbGV0ZShwcm9ncmVzc19sb2NrZG93biwgdGlkeXI6Om5lc3RpbmcoY291cnNlLCBsZXZlbCwgeWVhciwgY2hhcHRlcl9zaW1wbGUpLCBzY2hvb2xfeWVhciwgZmlsbCA9IGxpc3QodHJpYWxzID0gMCkpKSAKICAKcHJvZ3Jlc3NfbG9ja2Rvd25bLCBwcm9wIDo9IHRyaWFscy9zdW0odHJpYWxzKSwgYnkgPSAuKHNjaG9vbF95ZWFyLCBjb3Vyc2UsIGxldmVsLCB5ZWFyKV0KCnNldG9yZGVyKHByb2dyZXNzX2xvY2tkb3duLCBjaGFwdGVyX3NpbXBsZSkKYGBgCgpgYGB7cn0KZ2dwbG90KHByb2dyZXNzX2xvY2tkb3duW2NvdXJzZSA9PSAiRnJlbmNoIl0sIGFlcyh4ID0gc2Nob29sX3llYXIsIHkgPSBwcm9wLCBmaWxsID0gY2hhcHRlcl9zaW1wbGUsIGdyb3VwID0gc2Nob29sX3llYXIpKSArCiAgZmFjZXRfZ3JpZChsZXZlbCB+IHllYXIpICsKICBnZW9tX2NvbChjb2xvdXIgPSBOQSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KCksIGxpbWl0cyA9IGMoMCwxKSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gIlNoYXJlIG9mIHRyaWFscyIsCiAgICAgICBmaWxsID0gIkNoYXB0ZXIiLAogICAgICAgdGl0bGUgPSAiRnJlbmNoIikgKwogIHRoZW1lX3BhcGVyCmBgYAoKUGVyZm9ybSBhIGNoaS1zcXVhcmUgdGVzdCBvZiBob21vZ2VuZWl0eSB0byBkZXRlcm1pbmUgd2hldGhlciBzY2hvb2wgeWVhcnMgYXJlIHNpZ25pZmljYW50bHkgZGlmZmVyZW50LgoKYGBge3J9CmZvciAoeSBpbiBzb3J0KHVuaXF1ZShwcm9ncmVzc19sb2NrZG93biR5ZWFyKSkpIHsKICBmb3IgKGwgaW4gbGV2ZWxzKHByb2dyZXNzX2xvY2tkb3duJGxldmVsKSkgewogICAgZCA8LSBwcm9ncmVzc19sb2NrZG93bltjb3Vyc2UgPT0gIkZyZW5jaCIgJiB5ZWFyID09IHkgJiBsZXZlbCA9PSBsXQogICAgaWYgKG5yb3coZCkgPiAwKSB7CiAgICAgIHByaW50KHBhc3RlKCJGcmVuY2giLCB5LCBsLCBjb2xsYXBzZT0gIiAiKSkKICAgICAgcHJpbnQoCiAgICAgICAgY2hpc3EudGVzdCgKICAgICAgICAgIGRjYXN0KGQsIHNjaG9vbF95ZWFyIH4gY2hhcHRlcl9zaW1wbGUsIHZhbHVlLnZhciA9ICJ0cmlhbHMiLCBmaWxsID0gMClbLCBzY2hvb2xfeWVhciA6PSBOVUxMXQogICAgICAgICkKICAgICAgKQogICAgfQogIH0gCn0KYGBgCgpDb25jbHVzaW9uOiBhbGwgdGVzdHMgaW5kaWNhdGUgYSBkaWZmZXJlbmNlIGluIHByb3BvcnRpb25zIGJldHdlZW4gc2Nob29sIHllYXJzIChwIDw8IDAuMDAxKS4KClZpc3VhbGlzZSB0aGUgY2hhbmdlIGJldHdlZW4gc2Nob29sIHllYXJzOgpgYGB7cn0KcHJvZ3Jlc3NfbG9ja2Rvd25bLCBwcm9wX2NoYW5nZSA6PSBwcm9wW3NjaG9vbF95ZWFyID09ICIxOS8yMCJdIC0gcHJvcFtzY2hvb2xfeWVhciA9PSAiMTgvMTkiXSwgYnkgPSAuKGNvdXJzZSwgbGV2ZWwsIHllYXIsIGNoYXB0ZXJfc2ltcGxlKV0KCmdncGxvdChwcm9ncmVzc19sb2NrZG93bltzY2hvb2xfeWVhciA9PSAiMTkvMjAiICYgY291cnNlID09ICJGcmVuY2giXSwgYWVzKHggPSBjaGFwdGVyX3NpbXBsZSwgeSA9IChwcm9wX2NoYW5nZSAqIDEwMCksIGNvbG91ciA9IGNoYXB0ZXJfc2ltcGxlLCBncm91cCA9IHNjaG9vbF95ZWFyKSkgKwogIGZhY2V0X2dyaWQobGV2ZWwgfiB5ZWFyLCBzY2FsZXMgPSAiZnJlZV94IikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGx0eSA9IDIpICsKICBnZW9tX3NlZ21lbnQoYWVzKHhlbmQgPSBjaGFwdGVyX3NpbXBsZSksIHllbmQgPSAwKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygtMjUsIDI1KSkgKwogIHNjYWxlX2NvbG91cl92aXJpZGlzX2QoZGlyZWN0aW9uID0gLTEsIG5hLnRyYW5zbGF0ZSA9IEZBTFNFKSArCiAgZ3VpZGVzKGNvbG91ciA9IEZBTFNFKSArCiAgbGFicyh4ID0gIkNoYXB0ZXIiLAogICAgICAgeSA9ICJDaGFuZ2UgaW4gdHJpYWwgc2hhcmVcbihwZXJjZW50YWdlIHBvaW50cykiLAogICAgICAgdGl0bGUgPSAiRnJlbmNoIikgKwogIHRoZW1lX3BhcGVyCmBgYAoKQXJlIHRoZXNlIGNoYW5nZXMgcmVhbGx5IGltcG9ydGFudD8gV2UgbWF5IGV4cGVjdCBhIGNlcnRhaW4gYW1vdW50IG9mIGZsdWN0dWF0aW9uIGJldHdlZW4gYW55IHBhaXIgb2Ygc2Nob29sIHllYXJzLiBXZSBkb24ndCBoYXZlIGRhdGEgZnJvbSBiZWZvcmUgdGhlIDE4LzE5IHNjaG9vbCB5ZWFyLCBidXQgd2UgY2FuIGxvb2sgYXQgaG93IHRoZSBtYWduaXR1ZGUgb2YgY2hhbmdlcyBkdXJpbmcgdGhlIGxvY2tkb3duIHBlcmlvZCBjb21wYXJlcyB0byBjaGFuZ2VzIGVhcmxpZXIgaW4gdGhlIHNjaG9vbCB5ZWFyLgoKVG8ga2VlcCB0aGluZ3MgYXMgY29tcGFyYWJsZSBhcyBwb3NzaWJsZSwgdXNlIGEgc2xpZGluZyB0aW1lIHdpbmRvdyB3aXRoIHRoZSBzYW1lIHNpemUgYXMgdGhlIGxvY2tkb3duIHBlcmlvZDoKYGBge3J9CnRpbWVfd2luZG93IDwtIGFzLm51bWVyaWMocm91bmQoZGF0ZV9zY2hvb2xzX29wZW5lZCAtIGRhdGVfc2Nob29sc19jbG9zZWQpKQp0aW1lX3dpbmRvdwpgYGAKCmBgYHtyfQpkYXRlX3JhbmdlIDwtIHNvcnQodW5pcXVlKHByb2dyZXNzX2J5X2RheSRkb3lfcG9zaXhfYWxpZ25lZCkpCmRhdGVfcmFuZ2UgPC0gZGF0ZV9yYW5nZVtkYXRlX3JhbmdlIDwgZGF0ZV9zY2hvb2xzX2Nsb3NlZF0KCnByb3BfY2hhbmdlX3dpbmRvdyA8LSBkYXRhLnRhYmxlKCkKCmZvciAoaSBpbiAxOihsZW5ndGgoZGF0ZV9yYW5nZSkgLSBhcy5udW1lcmljKHRpbWVfd2luZG93KSkpIHsKICBkIDwtIGRhdGVfcmFuZ2VbaTooaSArIHRpbWVfd2luZG93IC0gMSldCiAgcHJvZ3Jlc3Nfd2luZG93IDwtIHByb2dyZXNzX2J5X2RheVtjb3Vyc2UgJWluJSBjKCJGcmVuY2giLCAiRW5nbGlzaCIpICYgZG95X3Bvc2l4X2FsaWduZWQgJWluJSBkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLih0cmlhbHMgPSBzdW0odHJpYWxzKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IC4oc2Nob29sX3llYXIsIGNvdXJzZSwgbGV2ZWwsIHllYXIsIGNoYXB0ZXJfc2ltcGxlKV0KICAKICAjIEZpbGwgaW4gbWlzc2luZyByb3dzIChvY2N1cnMgd2hlbiBjaGFwdGVyIHdhcyBvbmx5IHN0dWRpZWQgaW4gb25lIG9mIHRoZSB0d28geWVhcnMpCiAgcHJvZ3Jlc3Nfd2luZG93IDwtIGFzLmRhdGEudGFibGUodGlkeXI6OmNvbXBsZXRlKHByb2dyZXNzX3dpbmRvdywgdGlkeXI6Om5lc3RpbmcoY291cnNlLCBsZXZlbCwgeWVhciwgY2hhcHRlcl9zaW1wbGUpLCBzY2hvb2xfeWVhciwgZmlsbCA9IGxpc3QodHJpYWxzID0gMCkpKSAKICAKICBwcm9ncmVzc193aW5kb3dbLCBwcm9wIDo9IHRyaWFscy9zdW0odHJpYWxzKSwgYnkgPSAuKHNjaG9vbF95ZWFyLCBjb3Vyc2UsIGxldmVsLCB5ZWFyKV0KCiAgcHJvZ3Jlc3Nfd2luZG93WywgcHJvcF9jaGFuZ2UgOj0gcHJvcFtzY2hvb2xfeWVhciA9PSAiMTkvMjAiXSAtIHByb3Bbc2Nob29sX3llYXIgPT0gIjE4LzE5Il0sIGJ5ID0gLihjb3Vyc2UsIGxldmVsLCB5ZWFyLCBjaGFwdGVyX3NpbXBsZSldCiAgCiAgcHJvcF9jaGFuZ2Vfd2luZG93IDwtIHJiaW5kKHByb3BfY2hhbmdlX3dpbmRvdywgcHJvZ3Jlc3Nfd2luZG93W3NjaG9vbF95ZWFyID09ICIxOS8yMCIsXVssd2luZG93IDo9IGldWyxkYXRlX21pbiA6PSBtaW4oZCldWyxkYXRlX21heCA6PSBtYXgoZCldKQp9CmBgYAoKClRoZSBkZW5zaXR5IG9mIHllYXItdG8teWVhciBjaGFuZ2VzIGNhbiBiZSB2aXN1YWxpc2VkIGJ5IHRpbWUgd2luZG93OgpgYGB7cn0KZ2dwbG90KHByb3BfY2hhbmdlX3dpbmRvdywgYWVzKHggPSBwcm9wX2NoYW5nZSAqIDEwMCwgeSA9IHdpbmRvdywgZ3JvdXAgPSB3aW5kb3cpKSArCiAgZ2dyaWRnZXM6Omdlb21fZGVuc2l0eV9yaWRnZXMoYWxwaGEgPSAwLjEsIHNjYWxlID0gMjUsIGZpbGwgPSBOQSkgKwogIGxhYnMoeCA9ICJDaGFuZ2UgaW4gdHJpYWwgc2hhcmVcbihwZXJjZW50YWdlIHBvaW50cykiLAogICAgICAgICB5ID0gIlRpbWUgd2luZG93IikgKwogIHRoZW1lX3BhcGVyCmBgYAoKQ29tcGFyZSB0aGUgYWdncmVnYXRlZCBkZW5zaXR5IHRvIHRoZSBjaGFuZ2VzIGR1cmluZyB0aGUgbG9ja2Rvd24gcGVyaW9kOgpgYGB7cn0KcHJvcF9jaGFuZ2VfY29tYmluZWQgPC0gcmJpbmQocHJvcF9jaGFuZ2Vfd2luZG93WywgcGVyaW9kIDo9ICJQcmUtbG9ja2Rvd24iXSwgcHJvZ3Jlc3NfbG9ja2Rvd25bY291cnNlICVpbiUgYygiRnJlbmNoIiwgIkVuZ2xpc2giKSAmIHNjaG9vbF95ZWFyID09ICIxOS8yMCIsIHBlcmlvZCA6PSAiTG9ja2Rvd24iXSwgZmlsbCA9IFRSVUUpCnByb3BfY2hhbmdlX2NvbWJpbmVkWywgcGVyaW9kIDo9IGZhY3RvcihwZXJpb2QsIGxldmVscyA9IGMoIlByZS1sb2NrZG93biIsICJMb2NrZG93biIpKV0KCmdncGxvdChwcm9wX2NoYW5nZV9jb21iaW5lZCwgYWVzKHggPSBwcm9wX2NoYW5nZSwgY29sb3VyID0gcGVyaW9kKSkgKwogIGdlb21fZGVuc2l0eSgpICsKICBzY2FsZV9jb2xvdXJfdmlyaWRpc19kKGVuZCA9IC41LCBkaXJlY3Rpb24gPSAtMSwgbmEudHJhbnNsYXRlID0gRkFMU0UpICsKICBsYWJzKHggPSAiQ2hhbmdlIGluIHRyaWFsIHNoYXJlXG4ocGVyY2VudGFnZSBwb2ludHMpIiwKICAgICAgIHkgPSAiRGVuc2l0eSIsCiAgICAgICBjb2xvdXIgPSBOVUxMKSArCiAgdGhlbWVfcGFwZXIKYGBgCgpgYGB7cn0KcHJvcF9jaGFuZ2Vfc2QgPC0gcHJvcF9jaGFuZ2Vfd2luZG93WywgLihzZCA9IHNkKHByb3BfY2hhbmdlKSAqIDEwMCksIGJ5ID0gLihjb3Vyc2UsIHllYXIsIGxldmVsKV0KYGBgCgoKQWRkIGJvdW5kYXJpZXMgYmFzZWQgb24gdGhlIHR5cGljYWwgc3ByZWFkIHRvIHRoZSBjaGFuZ2UgcGxvdDoKYGBge3J9CnBfY2hhbmdlX2ZyZW5jaCA8LSBnZ3Bsb3QocHJvZ3Jlc3NfbG9ja2Rvd25bc2Nob29sX3llYXIgPT0gIjE5LzIwIiAmIGNvdXJzZSA9PSAiRnJlbmNoIl0sIGFlcyhjb2xvdXIgPSBjaGFwdGVyX3NpbXBsZSkpICsKICBmYWNldF9ncmlkKHllYXIgfiBsZXZlbCwgc2NhbGVzID0gImZyZWVfeCIpICsKICBnZW9tX3JlY3QoZGF0YSA9IHByb3BfY2hhbmdlX3NkW2NvdXJzZSA9PSAiRnJlbmNoIl0sIGFlcyh5bWluID0gLTIqc2QsIHltYXggPSAyKnNkKSwgeG1pbiA9IDAsIHhtYXggPSAxMDAwLCBmaWxsID0gImdyZXk5MCIsIGNvbG91ciA9IE5BKSArCiAgZ2VvbV9yZWN0KGRhdGEgPSBwcm9wX2NoYW5nZV9zZFtjb3Vyc2UgPT0gIkZyZW5jaCJdLCBhZXMoeW1pbiA9IC1zZCwgeW1heCA9IHNkKSwgeG1pbiA9IDAsIHhtYXggPSAxMDAsIGZpbGwgPSAiZ3JleTc1IiwgY29sb3VyID0gTkEpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsdHkgPSAyKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gY2hhcHRlcl9zaW1wbGUsIHhlbmQgPSBjaGFwdGVyX3NpbXBsZSwgeSA9IChwcm9wX2NoYW5nZSAqIDEwMCkpLCB5ZW5kID0gMCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBjaGFwdGVyX3NpbXBsZSwgeSA9IChwcm9wX2NoYW5nZSAqIDEwMCkpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IGMoLTIwLCAwLCAyMCkpICsKICBzY2FsZV9jb2xvdXJfdmlyaWRpc19kKGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIGd1aWRlcyhjb2xvdXIgPSBGQUxTRSkgKwogIGxhYnMoeCA9ICJDaGFwdGVyIiwKICAgICAgIHkgPSAiQ2hhbmdlIGluIHRyaWFsIHNoYXJlXG4ocGVyY2VudGFnZSBwb2ludHMpIikgKwogIHRoZW1lX3BhcGVyICsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCkpCgpwX2NoYW5nZV9mcmVuY2gKCmdnc2F2ZSgiLi4vb3V0cHV0L3Byb2dyZXNzX2NoYW5nZV9mcmVuY2gucGRmIiwgd2lkdGggPSA1LCBoZWlnaHQgPSA0KQpnZ3NhdmUoIi4uL291dHB1dC9wcm9ncmVzc19jaGFuZ2VfZnJlbmNoLmVwcyIsIHdpZHRoID0gNSwgaGVpZ2h0ID0gNCkKZ2dzYXZlKCIuLi9vdXRwdXQvcHJvZ3Jlc3NfY2hhbmdlX2ZyZW5jaC5wbmciLCB3aWR0aCA9IDksIGhlaWdodCA9IDMpCmBgYAoKTWFrZSBhIGNvbWJpbmF0aW9uIHBsb3QgZm9yIGluIHRoZSBwYXBlcjoKYGBge3J9CnBsb3RfZ3JpZChwX2ZyZW5jaF95MSwgcF9mcmVuY2hfeTIsIHBfZnJlbmNoX3kzLCBwX2NoYW5nZV9mcmVuY2gsCiAgICAgICAgICBuY29sID0gMSwKICAgICAgICAgIGFsaWduID0gImh2IiwgYXhpcyA9ICJ0YmxyIiwKICAgICAgICAgIGxhYmVscyA9IGMoIlllYXIgMSIsICJZZWFyIDIiLCAiWWVhciAzLzQiLCAiQ2hhbmdlIiksCiAgICAgICAgICByZWxfaGVpZ2h0cyA9IGMoMSwgMSwgMSwgMS41KSwKICAgICAgICAgIGhqdXN0ID0gLTAuMSwKICAgICAgICAgIHNjYWxlID0gLjk1KQoKZ2dzYXZlKCIuLi9vdXRwdXQvcHJvZ3Jlc3NfY29tYmlfZnJlbmNoLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKZ2dzYXZlKCIuLi9vdXRwdXQvcHJvZ3Jlc3NfY29tYmlfZnJlbmNoLmVwcyIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKZ2dzYXZlKCIuLi9vdXRwdXQvcHJvZ3Jlc3NfY29tYmlfZnJlbmNoLnBuZyIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKYGBgCgoKYGBge3J9CnBfY2hhbmdlX2ZyZW5jaF95MSA8LSBnZ3Bsb3QocHJvZ3Jlc3NfbG9ja2Rvd25bc2Nob29sX3llYXIgPT0gIjE5LzIwIiAmIGNvdXJzZSA9PSAiRnJlbmNoIiAmIHllYXIgPT0gIlllYXIgMSJdLCBhZXMoY29sb3VyID0gY2hhcHRlcl9zaW1wbGUpKSArCiAgZmFjZXRfZ3JpZCguIH4gbGV2ZWwsIHNjYWxlcyA9ICJmcmVlX3giKSArCiAgZ2VvbV9yZWN0KGRhdGEgPSBwcm9wX2NoYW5nZV9zZFtjb3Vyc2UgPT0gIkZyZW5jaCIgJiB5ZWFyID09ICJZZWFyIDEiXSwgYWVzKHltaW4gPSAtMipzZCwgeW1heCA9IDIqc2QpLCB4bWluID0gMCwgeG1heCA9IDEwMDAsIGZpbGwgPSAiZ3JleTkwIiwgY29sb3VyID0gTkEpICsKICBnZW9tX3JlY3QoZGF0YSA9IHByb3BfY2hhbmdlX3NkW2NvdXJzZSA9PSAiRnJlbmNoIiAmIHllYXIgPT0gIlllYXIgMSJdLCBhZXMoeW1pbiA9IC1zZCwgeW1heCA9IHNkKSwgeG1pbiA9IDAsIHhtYXggPSAxMDAsIGZpbGwgPSAiZ3JleTc1IiwgY29sb3VyID0gTkEpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsdHkgPSAyKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gY2hhcHRlcl9zaW1wbGUsIHhlbmQgPSBjaGFwdGVyX3NpbXBsZSwgeSA9IChwcm9wX2NoYW5nZSAqIDEwMCkpLCB5ZW5kID0gMCwgYWxwaGEgPSAuNzUpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gY2hhcHRlcl9zaW1wbGUsIHkgPSAocHJvcF9jaGFuZ2UgKiAxMDApKSwgYWxwaGEgPSAuNzUpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gYygtMjAsIDAsIDIwKSwgbGltaXRzID0gYygtMzAsIDMwKSwgbGFiZWxzID0gc2NhbGVzOjpudW1iZXJfZm9ybWF0KHN1ZmZpeCA9ICIgcHAiKSkgKwogIHNjYWxlX2NvbG91cl92aXJpZGlzX2QoZGlyZWN0aW9uID0gLTEsIG5hLnRyYW5zbGF0ZSA9IEZBTFNFKSArCiAgZ3VpZGVzKGNvbG91ciA9IEZBTFNFKSArCiAgbGFicyh4ID0gTlVMTCwKICAgICAgIHkgPSAiQ2hhbmdlIikgKwogIHRoZW1lX3BhcGVyICsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3VyID0gTkEpLAogICAgICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3VyID0gTkEpKQoKcF9jaGFuZ2VfZnJlbmNoX3kyIDwtIGdncGxvdChwcm9ncmVzc19sb2NrZG93bltzY2hvb2xfeWVhciA9PSAiMTkvMjAiICYgY291cnNlID09ICJGcmVuY2giICYgeWVhciA9PSAiWWVhciAyIl0sIGFlcyhjb2xvdXIgPSBjaGFwdGVyX3NpbXBsZSkpICsKICBmYWNldF9ncmlkKC4gfiBsZXZlbCwgc2NhbGVzID0gImZyZWVfeCIpICsKICBnZW9tX3JlY3QoZGF0YSA9IHByb3BfY2hhbmdlX3NkW2NvdXJzZSA9PSAiRnJlbmNoIiAmIHllYXIgPT0gIlllYXIgMiJdLCBhZXMoeW1pbiA9IC0yKnNkLCB5bWF4ID0gMipzZCksIHhtaW4gPSAwLCB4bWF4ID0gMTAwMCwgZmlsbCA9ICJncmV5OTAiLCBjb2xvdXIgPSBOQSkgKwogIGdlb21fcmVjdChkYXRhID0gcHJvcF9jaGFuZ2Vfc2RbY291cnNlID09ICJGcmVuY2giICYgeWVhciA9PSAiWWVhciAyIl0sIGFlcyh5bWluID0gLXNkLCB5bWF4ID0gc2QpLCB4bWluID0gMCwgeG1heCA9IDEwMCwgZmlsbCA9ICJncmV5NzUiLCBjb2xvdXIgPSBOQSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGx0eSA9IDIpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSBjaGFwdGVyX3NpbXBsZSwgeGVuZCA9IGNoYXB0ZXJfc2ltcGxlLCB5ID0gKHByb3BfY2hhbmdlICogMTAwKSksIHllbmQgPSAwLCBhbHBoYSA9IC43NSkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBjaGFwdGVyX3NpbXBsZSwgeSA9IChwcm9wX2NoYW5nZSAqIDEwMCkpLCBhbHBoYSA9IC43NSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBjKC0yMCwgMCwgMjApLCAgbGltaXRzID0gYygtMzAsIDMwKSwgbGFiZWxzID0gc2NhbGVzOjpudW1iZXJfZm9ybWF0KHN1ZmZpeCA9ICIgcHAiKSkgKwogIHNjYWxlX2NvbG91cl92aXJpZGlzX2QoZGlyZWN0aW9uID0gLTEsIG5hLnRyYW5zbGF0ZSA9IEZBTFNFKSArCiAgZ3VpZGVzKGNvbG91ciA9IEZBTFNFKSArCiAgbGFicyh4ID0gTlVMTCwKICAgICAgIHkgPSAiQ2hhbmdlIikgKwogIHRoZW1lX3BhcGVyICsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3VyID0gTkEpLAogICAgICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3VyID0gTkEpKQoKcF9jaGFuZ2VfZnJlbmNoX3kzIDwtIGdncGxvdChwcm9ncmVzc19sb2NrZG93bltzY2hvb2xfeWVhciA9PSAiMTkvMjAiICYgY291cnNlID09ICJGcmVuY2giICYgeWVhciA9PSAiWWVhciAzLzQiXSwgYWVzKGNvbG91ciA9IGNoYXB0ZXJfc2ltcGxlKSkgKwogIGZhY2V0X2dyaWQoLiB+IGxldmVsLCBzY2FsZXMgPSAiZnJlZV94IikgKwogIGdlb21fcmVjdChkYXRhID0gcHJvcF9jaGFuZ2Vfc2RbY291cnNlID09ICJGcmVuY2giICYgeWVhciA9PSAiWWVhciAzLzQiXSwgYWVzKHltaW4gPSAtMipzZCwgeW1heCA9IDIqc2QpLCB4bWluID0gMCwgeG1heCA9IDEwMDAsIGZpbGwgPSAiZ3JleTkwIiwgY29sb3VyID0gTkEpICsKICBnZW9tX3JlY3QoZGF0YSA9IHByb3BfY2hhbmdlX3NkW2NvdXJzZSA9PSAiRnJlbmNoIiAmIHllYXIgPT0gIlllYXIgMy80Il0sIGFlcyh5bWluID0gLXNkLCB5bWF4ID0gc2QpLCB4bWluID0gMCwgeG1heCA9IDEwMCwgZmlsbCA9ICJncmV5NzUiLCBjb2xvdXIgPSBOQSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGx0eSA9IDIpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSBjaGFwdGVyX3NpbXBsZSwgeGVuZCA9IGNoYXB0ZXJfc2ltcGxlLCB5ID0gKHByb3BfY2hhbmdlICogMTAwKSksIHllbmQgPSAwLCBhbHBoYSA9IC43NSkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBjaGFwdGVyX3NpbXBsZSwgeSA9IChwcm9wX2NoYW5nZSAqIDEwMCkpLCBhbHBoYSA9IC43NSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBjKC0yMCwgMCwgMjApLCAgbGltaXRzID0gYygtMzAsIDMwKSwgbGFiZWxzID0gc2NhbGVzOjpudW1iZXJfZm9ybWF0KHN1ZmZpeCA9ICIgcHAiKSkgKwogIHNjYWxlX2NvbG91cl92aXJpZGlzX2QoZGlyZWN0aW9uID0gLTEsIG5hLnRyYW5zbGF0ZSA9IEZBTFNFKSArCiAgZ3VpZGVzKGNvbG91ciA9IEZBTFNFKSArCiAgbGFicyh4ID0gTlVMTCwKICAgICAgIHkgPSAiQ2hhbmdlIikgKwogIHRoZW1lX3BhcGVyICsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3VyID0gTkEpLAogICAgICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3VyID0gTkEpKQoKZmlsbGVyX3Bsb3QgPC0gcXBsb3QoKSArIAogIHRoZW1lX25vdGhpbmcoKSArIAogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGNvbG91ciA9IE5BKSwKICAgICAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGNvbG91ciA9IE5BKSkKCnBsb3RfZ3JpZCgKICAgICAgICAgIGZpbGxlcl9wbG90LAogICAgICAgICAgcF9mcmVuY2hfeTEsIGZpbGxlcl9wbG90LCBwX2NoYW5nZV9mcmVuY2hfeTEsIGZpbGxlcl9wbG90LAogICAgICAgICAgcF9mcmVuY2hfeTIsIGZpbGxlcl9wbG90LCBwX2NoYW5nZV9mcmVuY2hfeTIsIGZpbGxlcl9wbG90LCAKICAgICAgICAgIHBfZnJlbmNoX3kzLCBmaWxsZXJfcGxvdCwgcF9jaGFuZ2VfZnJlbmNoX3kzLAogICAgICAgICAgbmNvbCA9IDEsCiAgICAgICAgICBhbGlnbiA9ICJodiIsIGF4aXMgPSAidGJsciIsCiAgICAgICAgICBsYWJlbHMgPSBjKE5BLAogICAgICAgICAgICAgICAgICAgICAiWWVhciAxIiwgTkEsIE5BLCBOQSwKICAgICAgICAgICAgICAgICAgICAgIlllYXIgMiIsIE5BLCBOQSwgTkEsCiAgICAgICAgICAgICAgICAgICAgICJZZWFyIDMvNCIsIE5BLCBOQSksCiAgICAgICAgICByZWxfaGVpZ2h0cyA9IGMoLjEsIAogICAgICAgICAgICAgICAgICAgICAgICAgIDEsIC0uMiwgLjc1LCAuMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAxLCAtLjIsIC43NSwgLjEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgMSwgLS4yLCAuNzUpLAogICAgICAgICAgaGp1c3QgPSAtMC4xLAogICAgICAgICAgdmp1c3QgPSAtMC4xLAogICAgICAgICAgc2NhbGUgPSAuOTUpCgpnZ3NhdmUoIi4uL291dHB1dC9wcm9ncmVzc19jb21iaV9hbHRfZnJlbmNoLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKZ2dzYXZlKCIuLi9vdXRwdXQvcHJvZ3Jlc3NfY29tYmlfYWx0X2ZyZW5jaC5lcHMiLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCmdnc2F2ZSgiLi4vb3V0cHV0L3Byb2dyZXNzX2NvbWJpX2FsdF9mcmVuY2gucG5nIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA5KQpgYGAKCgojIyMgRW5nbGlzaAoKTk9URTogY2hhcHRlcnMgd2l0aG91dCBhIG51bWJlciAoQnJpZGdpbmcgdGhlIEdhcCwgRXhhbSBQcmVwYXJhdGlvbikgYXJlIHNob3duIGFzICJPIiBpbiB0aGUgcGxvdC4KVGhleSBkb24ndCBzZWVtIHRvIGZpdCBuZWF0bHkgaW4gdGhlIGNoYXB0ZXIgc2VxdWVuY2UsIHNvIEknbSBncm91cGluZyB0aGVtIHRvZ2V0aGVyLgpgYGB7cn0KcF9lbmdsaXNoX3kxIDwtIGdncGxvdChwcm9ncmVzc19ieV93ZWVrW2xldmVsICE9ICJPdGhlciJdWywgbGV2ZWwgOj0gZmFjdG9yKGxldmVsKV1bY291cnNlID09ICJFbmdsaXNoIiAmIHllYXIgPT0gIlllYXIgMSJdLCBhZXMoeCA9IGFzLlBPU0lYY3QoZG95X3Bvc2l4X2FsaWduZWRfd2VlayksIHkgPSBwcm9wLCBmaWxsID0gY2hhcHRlcl9zaW1wbGUsIGdyb3VwID0gc2Nob29sX3llYXIpKSArCiAgZmFjZXRfZ3JpZChzY2hvb2xfeWVhciB+IGxldmVsLCBkcm9wID0gRkFMU0UpICsKICBnZW9tX2NvbChhbHBoYSA9IDAuNzUsIHdpZHRoID0gNyoyNCo2MCo2MCwgY29sb3VyID0gTkEpICsKICBnZW9tX3JlY3QoeG1pbiA9IGRhdGVfc2Nob29sc19jbG9zZWQsIHhtYXggPSBkYXRlX3NjaG9vbHNfb3BlbmVkLCB5bWluID0gLTAuMDEsIHltYXggPSAxLjAxLCBmaWxsID0gTkEsIGNvbG91ciA9ICJibGFjayIsIGx0eSA9IDIpICsKICBzY2FsZV94X2RhdGV0aW1lKGV4cGFuZCA9IGMoMCwgMCksIAogICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYXMuUE9TSVhjdChjKAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0xMC0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0xMi0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wMi0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNC0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNi0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBhcy5QT1NJWGN0KGMoIjIwMTktMDktMDEgMDI6MDA6MDAgQ0VUIiwgIjIwMjAtMDctMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgZGF0ZV9sYWJlbHMgPSAiJWIiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSwgbGltaXRzID0gYygwLDEpLCBicmVha3MgPSBjKDAsIC41ICwgMSkpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZChkaXJlY3Rpb24gPSAtMSwgbmEudHJhbnNsYXRlID0gRkFMU0UpICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChuY29sID0gMikpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJTaGFyZSBvZiB0cmlhbHMiLAogICAgICAgZmlsbCA9ICJDaGFwdGVyIikgKwogIHRoZW1lX3BhcGVyCgpwX2VuZ2xpc2hfeTIgPC0gZ2dwbG90KHByb2dyZXNzX2J5X3dlZWtbbGV2ZWwgIT0gIk90aGVyIl1bLCBsZXZlbCA6PSBmYWN0b3IobGV2ZWwpXVtjb3Vyc2UgPT0gIkVuZ2xpc2giICYgeWVhciA9PSAiWWVhciAyIl0sIGFlcyh4ID0gYXMuUE9TSVhjdChkb3lfcG9zaXhfYWxpZ25lZF93ZWVrKSwgeSA9IHByb3AsIGZpbGwgPSBjaGFwdGVyX3NpbXBsZSwgZ3JvdXAgPSBzY2hvb2xfeWVhcikpICsKICBmYWNldF9ncmlkKHNjaG9vbF95ZWFyIH4gbGV2ZWwsIGRyb3AgPSBGQUxTRSkgKwogIGdlb21fY29sKGFscGhhID0gMC43NSwgd2lkdGggPSA3KjI0KjYwKjYwLCBjb2xvdXIgPSBOQSkgKwogIGdlb21fcmVjdCh4bWluID0gZGF0ZV9zY2hvb2xzX2Nsb3NlZCwgeG1heCA9IGRhdGVfc2Nob29sc19vcGVuZWQsIHltaW4gPSAtMC4wMSwgeW1heCA9IDEuMDEsIGZpbGwgPSBOQSwgY29sb3VyID0gImJsYWNrIiwgbHR5ID0gMikgKwogIHNjYWxlX3hfZGF0ZXRpbWUoZXhwYW5kID0gYygwLCAwKSwgCiAgICAgICAgICAgICAgICAgICBicmVha3MgPSBhcy5QT1NJWGN0KGMoCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTEwLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTEyLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTAyLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA0LTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA2LTAxIDAyOjAwOjAwIENFVCIpKSwKICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGFzLlBPU0lYY3QoYygiMjAxOS0wOS0wMSAwMjowMDowMCBDRVQiLCAiMjAyMC0wNy0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdCgpLCBsaW1pdHMgPSBjKDAsMSksIGJyZWFrcyA9IGMoMCwgLjUgLCAxKSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAyKSkgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gIlNoYXJlIG9mIHRyaWFscyIsCiAgICAgICBmaWxsID0gIkNoYXB0ZXIiKSArCiAgdGhlbWVfcGFwZXIKCnBfZW5nbGlzaF95MyA8LSBnZ3Bsb3QocHJvZ3Jlc3NfYnlfd2Vla1tsZXZlbCAhPSAiT3RoZXIiXVssIGxldmVsIDo9IGZhY3RvcihsZXZlbCldW2NvdXJzZSA9PSAiRW5nbGlzaCIgJiB5ZWFyID09ICJZZWFyIDMiXSwgYWVzKHggPSBhcy5QT1NJWGN0KGRveV9wb3NpeF9hbGlnbmVkX3dlZWspLCB5ID0gcHJvcCwgZmlsbCA9IGNoYXB0ZXJfc2ltcGxlLCBncm91cCA9IHNjaG9vbF95ZWFyKSkgKwogIGZhY2V0X2dyaWQoc2Nob29sX3llYXIgfiBsZXZlbCwgZHJvcCA9IEZBTFNFKSArCiAgZ2VvbV9jb2woYWxwaGEgPSAwLjc1LCB3aWR0aCA9IDcqMjQqNjAqNjAsIGNvbG91ciA9IE5BKSArCiAgZ2VvbV9yZWN0KHhtaW4gPSBkYXRlX3NjaG9vbHNfY2xvc2VkLCB4bWF4ID0gZGF0ZV9zY2hvb2xzX29wZW5lZCwgeW1pbiA9IC0wLjAxLCB5bWF4ID0gMS4wMSwgZmlsbCA9IE5BLCBjb2xvdXIgPSAiYmxhY2siLCBsdHkgPSAyKSArCiAgc2NhbGVfeF9kYXRldGltZShleHBhbmQgPSBjKDAsIDApLCAKICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGFzLlBPU0lYY3QoYygKICAgICAgICAgICAgICAgICAgICAgIjIwMTktMTAtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMTktMTItMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDItMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDQtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDYtMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgbGltaXRzID0gYXMuUE9TSVhjdChjKCIyMDE5LTA5LTAxIDAyOjAwOjAwIENFVCIsICIyMDIwLTA3LTAxIDAyOjAwOjAwIENFVCIpKSwKICAgICAgICAgICAgICAgICAgIGRhdGVfbGFiZWxzID0gIiViIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KCksIGxpbWl0cyA9IGMoMCwxKSwgYnJlYWtzID0gYygwLCAuNSAsIDEpKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoZGlyZWN0aW9uID0gLTEsIG5hLnRyYW5zbGF0ZSA9IEZBTFNFKSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQobmNvbCA9IDIpKSArCiAgbGFicyh4ID0gTlVMTCwKICAgICAgIHkgPSAiU2hhcmUgb2YgdHJpYWxzIiwKICAgICAgIGZpbGwgPSAiQ2hhcHRlciIpICsKICB0aGVtZV9wYXBlcgoKcF9lbmdsaXNoX3k0IDwtIGdncGxvdChwcm9ncmVzc19ieV93ZWVrW2xldmVsICE9ICJPdGhlciJdWywgbGV2ZWwgOj0gZmFjdG9yKGxldmVsKV1bY291cnNlID09ICJFbmdsaXNoIiAmIHllYXIgPT0gIlllYXIgNCJdLCBhZXMoeCA9IGFzLlBPU0lYY3QoZG95X3Bvc2l4X2FsaWduZWRfd2VlayksIHkgPSBwcm9wLCBmaWxsID0gY2hhcHRlcl9zaW1wbGUsIGdyb3VwID0gc2Nob29sX3llYXIpKSArCiAgZmFjZXRfZ3JpZChzY2hvb2xfeWVhciB+IGxldmVsLCBkcm9wID0gRkFMU0UpICsKICBnZW9tX2NvbChhbHBoYSA9IDAuNzUsIHdpZHRoID0gNyoyNCo2MCo2MCwgY29sb3VyID0gTkEpICsKICBnZW9tX3JlY3QoeG1pbiA9IGRhdGVfc2Nob29sc19jbG9zZWQsIHhtYXggPSBkYXRlX3NjaG9vbHNfb3BlbmVkLCB5bWluID0gLTAuMDEsIHltYXggPSAxLjAxLCBmaWxsID0gTkEsIGNvbG91ciA9ICJibGFjayIsIGx0eSA9IDIpICsKICBzY2FsZV94X2RhdGV0aW1lKGV4cGFuZCA9IGMoMCwgMCksIAogICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYXMuUE9TSVhjdChjKAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0xMC0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0xMi0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wMi0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNC0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNi0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBhcy5QT1NJWGN0KGMoIjIwMTktMDktMDEgMDI6MDA6MDAgQ0VUIiwgIjIwMjAtMDctMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgZGF0ZV9sYWJlbHMgPSAiJWIiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSwgbGltaXRzID0gYygwLDEpLCBicmVha3MgPSBjKDAsIC41ICwgMSkpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZChkaXJlY3Rpb24gPSAtMSwgbmEudHJhbnNsYXRlID0gRkFMU0UpICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChuY29sID0gMikpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJTaGFyZSBvZiB0cmlhbHMiLAogICAgICAgZmlsbCA9ICJDaGFwdGVyIikgKwogIHRoZW1lX3BhcGVyCgpwX3Byb2dyZXNzX2VuZ2xpc2ggPC0gcGxvdF9ncmlkKHBfZW5nbGlzaF95MSwgcF9lbmdsaXNoX3kyLCBwX2VuZ2xpc2hfeTMsIHBfZW5nbGlzaF95NCwKICAgICAgICAgIG5jb2wgPSAxLAogICAgICAgICAgYWxpZ24gPSAiaHYiLCBheGlzID0gInRibHIiLAogICAgICAgICAgbGFiZWxzID0gYygiWWVhciAxIiwgIlllYXIgMiIsICJZZWFyIDMiLCAiWWVhciA0IiksCiAgICAgICAgICBoanVzdCA9IC0wLjEsCiAgICAgICAgICBzY2FsZSA9IC45NSkKCnBfcHJvZ3Jlc3NfZW5nbGlzaAoKZ2dzYXZlKCIuLi9vdXRwdXQvcHJvZ3Jlc3NfZW5nbGlzaC5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDkpCmdnc2F2ZSgiLi4vb3V0cHV0L3Byb2dyZXNzX2VuZ2xpc2guZXBzIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA5KQpnZ3NhdmUoIi4uL291dHB1dC9wcm9ncmVzc19lbmdsaXNoLnBuZyIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gOSkKYGBgCgpEaWQgdGhlIHNoYXJlIG9mIHRyaWFscyBjaGFuZ2UgYmV0d2VlbiBzY2hvb2wgeWVhcnM/CgpgYGB7cn0KZ2dwbG90KHByb2dyZXNzX2xvY2tkb3duW2NvdXJzZSA9PSAiRW5nbGlzaCIgJiBsZXZlbCAhPSAiT3RoZXIiXSwgYWVzKHggPSBzY2hvb2xfeWVhciwgeSA9IHByb3AsIGZpbGwgPSBjaGFwdGVyX3NpbXBsZSwgZ3JvdXAgPSBzY2hvb2xfeWVhcikpICsKICBmYWNldF9ncmlkKGxldmVsIH4geWVhcikgKwogIGdlb21fY29sKGNvbG91ciA9IE5BKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoKSwgbGltaXRzID0gYygwLDEpKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoZGlyZWN0aW9uID0gLTEsIG5hLnRyYW5zbGF0ZSA9IEZBTFNFKSArCiAgbGFicyh4ID0gTlVMTCwKICAgICAgIHkgPSAiU2hhcmUgb2YgdHJpYWxzIiwKICAgICAgIGZpbGwgPSAiQ2hhcHRlciIsCiAgICAgICB0aXRsZSA9ICJFbmdsaXNoIikgKwogIHRoZW1lX3BhcGVyCmBgYAoKQ2hhbmdlIGJldHdlZW4gc2Nob29sIHllYXJzOgpgYGB7cn0KcF9jaGFuZ2VfZW5nbGlzaCA8LSBnZ3Bsb3QocHJvZ3Jlc3NfbG9ja2Rvd25bc2Nob29sX3llYXIgPT0gIjE5LzIwIiAmIGNvdXJzZSA9PSAiRW5nbGlzaCIgJiBsZXZlbCAhPSAiT3RoZXIiXSwgYWVzKGNvbG91ciA9IGNoYXB0ZXJfc2ltcGxlKSkgKwogIGZhY2V0X2dyaWQoeWVhciB+IGxldmVsLCBzY2FsZXMgPSAiZnJlZV94IikgKwogIGdlb21fcmVjdChkYXRhID0gcHJvcF9jaGFuZ2Vfc2RbY291cnNlID09ICJFbmdsaXNoIiAmIGxldmVsICE9ICJPdGhlciJdLCBhZXMoeW1pbiA9IC0yKnNkLCB5bWF4ID0gMipzZCksIHhtaW4gPSAwLCB4bWF4ID0gMTAwMCwgZmlsbCA9ICJncmV5OTAiLCBjb2xvdXIgPSBOQSkgKwogIGdlb21fcmVjdChkYXRhID0gcHJvcF9jaGFuZ2Vfc2RbY291cnNlID09ICJFbmdsaXNoIiAmIGxldmVsICE9ICJPdGhlciJdLCBhZXMoeW1pbiA9IC1zZCwgeW1heCA9IHNkKSwgeG1pbiA9IDAsIHhtYXggPSAxMDAsIGZpbGwgPSAiZ3JleTc1IiwgY29sb3VyID0gTkEpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsdHkgPSAyKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gY2hhcHRlcl9zaW1wbGUsIHhlbmQgPSBjaGFwdGVyX3NpbXBsZSwgeSA9IChwcm9wX2NoYW5nZSAqIDEwMCkpLCB5ZW5kID0gMCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBjaGFwdGVyX3NpbXBsZSwgeSA9IChwcm9wX2NoYW5nZSAqIDEwMCkpKSArCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXNfZChkaXJlY3Rpb24gPSAtMSwgbmEudHJhbnNsYXRlID0gRkFMU0UpICsKICBndWlkZXMoY29sb3VyID0gRkFMU0UpICsKICBsYWJzKHggPSAiQ2hhcHRlciIsCiAgICAgICB5ID0gIkNoYW5nZSBpbiB0cmlhbCBzaGFyZVxuKHBlcmNlbnRhZ2UgcG9pbnRzKSIpICsKICB0aGVtZV9wYXBlciArCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpKQoKcF9jaGFuZ2VfZW5nbGlzaCAKCmdnc2F2ZSgiLi4vb3V0cHV0L3Byb2dyZXNzX2NoYW5nZV9lbmdsaXNoLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gNikKZ2dzYXZlKCIuLi9vdXRwdXQvcHJvZ3Jlc3NfY2hhbmdlX2VuZ2xpc2guZXBzIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA2KQpnZ3NhdmUoIi4uL291dHB1dC9wcm9ncmVzc19jaGFuZ2VfZW5nbGlzaC5wbmciLCB3aWR0aCA9IDksIGhlaWdodCA9IDYpCmBgYAoKUGVyZm9ybSBhIGNoaS1zcXVhcmUgdGVzdCBvZiBob21vZ2VuZWl0eSB0byBkZXRlcm1pbmUgd2hldGhlciBzY2hvb2wgeWVhcnMgYXJlIHNpZ25pZmljYW50bHkgZGlmZmVyZW50LgoKYGBge3J9CmZvciAoeSBpbiBzb3J0KHVuaXF1ZShwcm9ncmVzc19sb2NrZG93biR5ZWFyKSkpIHsKICBmb3IgKGwgaW4gbGV2ZWxzKHByb2dyZXNzX2xvY2tkb3duJGxldmVsKSkgewogICAgZCA8LSBwcm9ncmVzc19sb2NrZG93bltjb3Vyc2UgPT0gIkVuZ2xpc2giICYgeWVhciA9PSB5ICYgbGV2ZWwgPT0gbF0KICAgIGlmIChucm93KGQpID4gMCkgewogICAgICBwcmludChwYXN0ZSgiRW5nbGlzaCIsIHksIGwsIGNvbGxhcHNlPSAiICIpKQogICAgICBwcmludCgKICAgICAgICBjaGlzcS50ZXN0KAogICAgICAgICAgZGNhc3QoZCwgc2Nob29sX3llYXIgfiBjaGFwdGVyX3NpbXBsZSwgdmFsdWUudmFyID0gInRyaWFscyIsIGZpbGwgPSAwKVssIHNjaG9vbF95ZWFyIDo9IE5VTExdCiAgICAgICAgKQogICAgICApCiAgICB9CiAgfSAKfQpgYGAKCkNvbmNsdXNpb246IGFsbCB0ZXN0cyBpbmRpY2F0ZSBhIGRpZmZlcmVuY2UgaW4gcHJvcG9ydGlvbnMgYmV0d2VlbiBzY2hvb2wgeWVhcnMgKHAgPDwgMC4wMDEpLgoKCk1ha2UgYSBjb21iaW5hdGlvbiBwbG90IGZvciBpbiB0aGUgcGFwZXI6CmBgYHtyfQpwcm9ncmVzc19sb2NrZG93bl9lbmdsaXNoIDwtIHByb2dyZXNzX2xvY2tkb3duW2xldmVsICE9ICJPdGhlciIgJiBzY2hvb2xfeWVhciA9PSAiMTkvMjAiICYgY291cnNlID09ICJFbmdsaXNoIl0KcHJvZ3Jlc3NfbG9ja2Rvd25fZW5nbGlzaFssIGxldmVsIDo9IGZhY3RvcihsZXZlbCldCgpwX2NoYW5nZV9lbmdsaXNoX3kxIDwtIGdncGxvdChwcm9ncmVzc19sb2NrZG93bl9lbmdsaXNoW3llYXIgPT0gIlllYXIgMSJdLCBhZXMoY29sb3VyID0gY2hhcHRlcl9zaW1wbGUpKSArCiAgZmFjZXRfZ3JpZCguIH4gbGV2ZWwsIHNjYWxlcyA9ICJmcmVlX3giLCBkcm9wID0gRkFMU0UpICsKICBnZW9tX3JlY3QoZGF0YSA9IHByb3BfY2hhbmdlX3NkW2NvdXJzZSA9PSAiRW5nbGlzaCIgJiBsZXZlbCAhPSAiT3RoZXIiICYgeWVhciA9PSAiWWVhciAxIl1bLGxldmVsIDo9IGZhY3RvcihsZXZlbCldLCBhZXMoeW1pbiA9IC0yKnNkLCB5bWF4ID0gMipzZCksIHhtaW4gPSAwLCB4bWF4ID0gMTAwMCwgZmlsbCA9ICJncmV5OTAiLCBjb2xvdXIgPSBOQSkgKwogIGdlb21fcmVjdChkYXRhID0gcHJvcF9jaGFuZ2Vfc2RbY291cnNlID09ICJFbmdsaXNoIiAmIGxldmVsICE9ICJPdGhlciIgJiB5ZWFyID09ICJZZWFyIDEiXVssbGV2ZWwgOj0gZmFjdG9yKGxldmVsKV0sIGFlcyh5bWluID0gLXNkLCB5bWF4ID0gc2QpLCB4bWluID0gMCwgeG1heCA9IDEwMCwgZmlsbCA9ICJncmV5NzUiLCBjb2xvdXIgPSBOQSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGx0eSA9IDIpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSBjaGFwdGVyX3NpbXBsZSwgeGVuZCA9IGNoYXB0ZXJfc2ltcGxlLCB5ID0gKHByb3BfY2hhbmdlICogMTAwKSksIHllbmQgPSAwLCBhbHBoYSA9IC43NSkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBjaGFwdGVyX3NpbXBsZSwgeSA9IChwcm9wX2NoYW5nZSAqIDEwMCkpLCBhbHBoYSA9IC43NSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBjKC0xMCwgMCwgMTApLCBsaW1pdHMgPSBjKC0yMCwgMjApLCBsYWJlbHMgPSBzY2FsZXM6Om51bWJlcl9mb3JtYXQoc3VmZml4ID0gIiBwcCIpKSArCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXNfZChkaXJlY3Rpb24gPSAtMSwgbmEudHJhbnNsYXRlID0gRkFMU0UpICsKICBndWlkZXMoY29sb3VyID0gRkFMU0UpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJDaGFuZ2UiKSArCiAgdGhlbWVfcGFwZXIgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvdXIgPSBOQSksCiAgICAgICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvdXIgPSBOQSkpCgpwX2NoYW5nZV9lbmdsaXNoX3kyIDwtIGdncGxvdChwcm9ncmVzc19sb2NrZG93bl9lbmdsaXNoW3llYXIgPT0gIlllYXIgMiJdLCBhZXMoY29sb3VyID0gY2hhcHRlcl9zaW1wbGUpKSArCiAgZmFjZXRfZ3JpZCguIH4gbGV2ZWwsIHNjYWxlcyA9ICJmcmVlX3giLCBkcm9wID0gRkFMU0UpICsKICBnZW9tX3JlY3QoZGF0YSA9IHByb3BfY2hhbmdlX3NkW2NvdXJzZSA9PSAiRW5nbGlzaCIgJiBsZXZlbCAhPSAiT3RoZXIiICYgeWVhciA9PSAiWWVhciAyIl1bLGxldmVsIDo9IGZhY3RvcihsZXZlbCldLCBhZXMoeW1pbiA9IC0yKnNkLCB5bWF4ID0gMipzZCksIHhtaW4gPSAwLCB4bWF4ID0gMTAwMCwgZmlsbCA9ICJncmV5OTAiLCBjb2xvdXIgPSBOQSkgKwogIGdlb21fcmVjdChkYXRhID0gcHJvcF9jaGFuZ2Vfc2RbY291cnNlID09ICJFbmdsaXNoIiAmIGxldmVsICE9ICJPdGhlciIgJiB5ZWFyID09ICJZZWFyIDIiXVssbGV2ZWwgOj0gZmFjdG9yKGxldmVsKV0sIGFlcyh5bWluID0gLXNkLCB5bWF4ID0gc2QpLCB4bWluID0gMCwgeG1heCA9IDEwMCwgZmlsbCA9ICJncmV5NzUiLCBjb2xvdXIgPSBOQSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGx0eSA9IDIpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSBjaGFwdGVyX3NpbXBsZSwgeGVuZCA9IGNoYXB0ZXJfc2ltcGxlLCB5ID0gKHByb3BfY2hhbmdlICogMTAwKSksIHllbmQgPSAwLCBhbHBoYSA9IC43NSkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBjaGFwdGVyX3NpbXBsZSwgeSA9IChwcm9wX2NoYW5nZSAqIDEwMCkpLCBhbHBoYSA9IC43NSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBjKC0xMCwgMCwgMTApLCBsaW1pdHMgPSBjKC0yMCwgMjApLCBsYWJlbHMgPSBzY2FsZXM6Om51bWJlcl9mb3JtYXQoc3VmZml4ID0gIiBwcCIpKSArCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXNfZChkaXJlY3Rpb24gPSAtMSwgbmEudHJhbnNsYXRlID0gRkFMU0UpICsKICBndWlkZXMoY29sb3VyID0gRkFMU0UpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJDaGFuZ2UiKSArCiAgdGhlbWVfcGFwZXIgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvdXIgPSBOQSksCiAgICAgICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvdXIgPSBOQSkpCgpwX2NoYW5nZV9lbmdsaXNoX3kzIDwtIGdncGxvdChwcm9ncmVzc19sb2NrZG93bl9lbmdsaXNoW3llYXIgPT0gIlllYXIgMyJdLCBhZXMoY29sb3VyID0gY2hhcHRlcl9zaW1wbGUpKSArCiAgZmFjZXRfZ3JpZCguIH4gbGV2ZWwsIHNjYWxlcyA9ICJmcmVlX3giLCBkcm9wID0gRkFMU0UpICsKICBnZW9tX3JlY3QoZGF0YSA9IHByb3BfY2hhbmdlX3NkW2NvdXJzZSA9PSAiRW5nbGlzaCIgJiBsZXZlbCAhPSAiT3RoZXIiICYgeWVhciA9PSAiWWVhciAzIl1bLGxldmVsIDo9IGZhY3RvcihsZXZlbCldLCBhZXMoeW1pbiA9IC0yKnNkLCB5bWF4ID0gMipzZCksIHhtaW4gPSAwLCB4bWF4ID0gMTAwMCwgZmlsbCA9ICJncmV5OTAiLCBjb2xvdXIgPSBOQSkgKwogIGdlb21fcmVjdChkYXRhID0gcHJvcF9jaGFuZ2Vfc2RbY291cnNlID09ICJFbmdsaXNoIiAmIGxldmVsICE9ICJPdGhlciIgJiB5ZWFyID09ICJZZWFyIDMiXVssbGV2ZWwgOj0gZmFjdG9yKGxldmVsKV0sIGFlcyh5bWluID0gLXNkLCB5bWF4ID0gc2QpLCB4bWluID0gMCwgeG1heCA9IDEwMCwgZmlsbCA9ICJncmV5NzUiLCBjb2xvdXIgPSBOQSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGx0eSA9IDIpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSBjaGFwdGVyX3NpbXBsZSwgeGVuZCA9IGNoYXB0ZXJfc2ltcGxlLCB5ID0gKHByb3BfY2hhbmdlICogMTAwKSksIHllbmQgPSAwLCBhbHBoYSA9IC43NSkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBjaGFwdGVyX3NpbXBsZSwgeSA9IChwcm9wX2NoYW5nZSAqIDEwMCkpLCBhbHBoYSA9IC43NSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBjKC0xMCwgMCwgMTApLCBsaW1pdHMgPSBjKC0yMCwgMjApLCBsYWJlbHMgPSBzY2FsZXM6Om51bWJlcl9mb3JtYXQoc3VmZml4ID0gIiBwcCIpKSArCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXNfZChkaXJlY3Rpb24gPSAtMSwgbmEudHJhbnNsYXRlID0gRkFMU0UpICsKICBndWlkZXMoY29sb3VyID0gRkFMU0UpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJDaGFuZ2UiKSArCiAgdGhlbWVfcGFwZXIgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvdXIgPSBOQSksCiAgICAgICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvdXIgPSBOQSkpCgpwX2NoYW5nZV9lbmdsaXNoX3k0IDwtIGdncGxvdChwcm9ncmVzc19sb2NrZG93bl9lbmdsaXNoW3llYXIgPT0gIlllYXIgNCJdLCBhZXMoY29sb3VyID0gY2hhcHRlcl9zaW1wbGUpKSArCiAgZmFjZXRfZ3JpZCguIH4gbGV2ZWwsIHNjYWxlcyA9ICJmcmVlX3giLCBkcm9wID0gRkFMU0UpICsKICBnZW9tX3JlY3QoZGF0YSA9IHByb3BfY2hhbmdlX3NkW2NvdXJzZSA9PSAiRW5nbGlzaCIgJiBsZXZlbCAhPSAiT3RoZXIiICYgeWVhciA9PSAiWWVhciA0Il1bLGxldmVsIDo9IGZhY3RvcihsZXZlbCldLCBhZXMoeW1pbiA9IC0yKnNkLCB5bWF4ID0gMipzZCksIHhtaW4gPSAwLCB4bWF4ID0gMTAwMCwgZmlsbCA9ICJncmV5OTAiLCBjb2xvdXIgPSBOQSkgKwogIGdlb21fcmVjdChkYXRhID0gcHJvcF9jaGFuZ2Vfc2RbY291cnNlID09ICJFbmdsaXNoIiAmIGxldmVsICE9ICJPdGhlciIgJiB5ZWFyID09ICJZZWFyIDQiXVssbGV2ZWwgOj0gZmFjdG9yKGxldmVsKV0sIGFlcyh5bWluID0gLXNkLCB5bWF4ID0gc2QpLCB4bWluID0gMCwgeG1heCA9IDEwMCwgZmlsbCA9ICJncmV5NzUiLCBjb2xvdXIgPSBOQSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGx0eSA9IDIpICsKICBnZW9tX3NlZ21lbnQoYWVzKHggPSBjaGFwdGVyX3NpbXBsZSwgeGVuZCA9IGNoYXB0ZXJfc2ltcGxlLCB5ID0gKHByb3BfY2hhbmdlICogMTAwKSksIHllbmQgPSAwLCBhbHBoYSA9IC43NSkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBjaGFwdGVyX3NpbXBsZSwgeSA9IChwcm9wX2NoYW5nZSAqIDEwMCkpLCBhbHBoYSA9IC43NSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBjKC0xMCwgMCwgMTApLCBsaW1pdHMgPSBjKC0yMCwgMjApLCBsYWJlbHMgPSBzY2FsZXM6Om51bWJlcl9mb3JtYXQoc3VmZml4ID0gIiBwcCIpKSArCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXNfZChkaXJlY3Rpb24gPSAtMSwgbmEudHJhbnNsYXRlID0gRkFMU0UpICsKICBndWlkZXMoY29sb3VyID0gRkFMU0UpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJDaGFuZ2UiKSArCiAgdGhlbWVfcGFwZXIgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvdXIgPSBOQSksCiAgICAgICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvdXIgPSBOQSkpCgpmaWxsZXJfcGxvdCA8LSBxcGxvdCgpICsgCiAgdGhlbWVfbm90aGluZygpICsgCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3VyID0gTkEpLAogICAgICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3VyID0gTkEpKQoKcGxvdF9ncmlkKAogICAgICAgICAgZmlsbGVyX3Bsb3QsCiAgICAgICAgICBwX2VuZ2xpc2hfeTEsIGZpbGxlcl9wbG90LCBwX2NoYW5nZV9lbmdsaXNoX3kxLCBmaWxsZXJfcGxvdCwKICAgICAgICAgIHBfZW5nbGlzaF95MiwgZmlsbGVyX3Bsb3QsIHBfY2hhbmdlX2VuZ2xpc2hfeTIsIGZpbGxlcl9wbG90LCAKICAgICAgICAgIHBfZW5nbGlzaF95MywgZmlsbGVyX3Bsb3QsIHBfY2hhbmdlX2VuZ2xpc2hfeTMsIGZpbGxlcl9wbG90LAogICAgICAgICAgcF9lbmdsaXNoX3k0LCBmaWxsZXJfcGxvdCwgcF9jaGFuZ2VfZW5nbGlzaF95NCwKICAgICAgICAgIG5jb2wgPSAxLAogICAgICAgICAgYWxpZ24gPSAiaHYiLCBheGlzID0gInRibHIiLAogICAgICAgICAgbGFiZWxzID0gYyhOQSwKICAgICAgICAgICAgICAgICAgICAgIlllYXIgMSIsIE5BLCBOQSwgTkEsCiAgICAgICAgICAgICAgICAgICAgICJZZWFyIDIiLCBOQSwgTkEsIE5BLAogICAgICAgICAgICAgICAgICAgICAiWWVhciAzIiwgTkEsIE5BLCBOQSwKICAgICAgICAgICAgICAgICAgICAgIlllYXIgNCIsIE5BLCBOQSksCiAgICAgICAgICByZWxfaGVpZ2h0cyA9IGMoLjEsIAogICAgICAgICAgICAgICAgICAgICAgICAgIDEsIC0uMiwgLjc1LCAuMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAxLCAtLjIsIC43NSwgLjEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgMSwgLS4yLCAuNzUsIC4xLAogICAgICAgICAgICAgICAgICAgICAgICAgIDEsIC0uMiwgLjc1KSwKICAgICAgICAgIGhqdXN0ID0gLTAuMSwKICAgICAgICAgIHZqdXN0ID0gLTAuMSwKICAgICAgICAgIHNjYWxlID0gLjk1KQoKZ2dzYXZlKCIuLi9vdXRwdXQvcHJvZ3Jlc3NfY29tYmlfYWx0X2VuZ2xpc2gucGRmIiwgd2lkdGggPSA5LCBoZWlnaHQgPSAxMSkKZ2dzYXZlKCIuLi9vdXRwdXQvcHJvZ3Jlc3NfY29tYmlfYWx0X2VuZ2xpc2guZXBzIiwgd2lkdGggPSA5LCBoZWlnaHQgPSAxMSkKZ2dzYXZlKCIuLi9vdXRwdXQvcHJvZ3Jlc3NfY29tYmlfYWx0X2VuZ2xpc2gucG5nIiwgd2lkdGggPSA5LCBoZWlnaHQgPSAxMSkKYGBgCgojIFNlc3Npb24gaW5mbwpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAKCg==